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 <stddef.h>
6 #include <stdint.h>
7 
8 #include "base/stl_util.h"
9 #include "base/values.h"
10 #include "components/webcrypto/algorithm_dispatch.h"
11 #include "components/webcrypto/algorithms/test_helpers.h"
12 #include "components/webcrypto/crypto_data.h"
13 #include "components/webcrypto/status.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
16 #include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
17 
18 namespace webcrypto {
19 
20 namespace {
21 
22 // Creates an AES-GCM algorithm.
CreateAesGcmAlgorithm(const std::vector<uint8_t> & iv,const std::vector<uint8_t> & additional_data,unsigned int tag_length_bits)23 blink::WebCryptoAlgorithm CreateAesGcmAlgorithm(
24     const std::vector<uint8_t>& iv,
25     const std::vector<uint8_t>& additional_data,
26     unsigned int tag_length_bits) {
27   return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
28       blink::kWebCryptoAlgorithmIdAesGcm,
29       new blink::WebCryptoAesGcmParams(iv, true, additional_data, true,
30                                        tag_length_bits));
31 }
32 
CreateAesGcmKeyGenAlgorithm(uint16_t key_length_bits)33 blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm(
34     uint16_t key_length_bits) {
35   return CreateAesKeyGenAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm,
36                                   key_length_bits);
37 }
38 
AesGcmEncrypt(const blink::WebCryptoKey & key,const std::vector<uint8_t> & iv,const std::vector<uint8_t> & additional_data,unsigned int tag_length_bits,const std::vector<uint8_t> & plain_text,std::vector<uint8_t> * cipher_text,std::vector<uint8_t> * authentication_tag)39 Status AesGcmEncrypt(const blink::WebCryptoKey& key,
40                      const std::vector<uint8_t>& iv,
41                      const std::vector<uint8_t>& additional_data,
42                      unsigned int tag_length_bits,
43                      const std::vector<uint8_t>& plain_text,
44                      std::vector<uint8_t>* cipher_text,
45                      std::vector<uint8_t>* authentication_tag) {
46   blink::WebCryptoAlgorithm algorithm =
47       CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
48 
49   std::vector<uint8_t> output;
50   Status status = Encrypt(algorithm, key, CryptoData(plain_text), &output);
51   if (status.IsError())
52     return status;
53 
54   if ((tag_length_bits % 8) != 0) {
55     ADD_FAILURE() << "Encrypt should have failed.";
56     return Status::OperationError();
57   }
58 
59   size_t tag_length_bytes = tag_length_bits / 8;
60 
61   if (tag_length_bytes > output.size()) {
62     ADD_FAILURE() << "tag length is larger than output";
63     return Status::OperationError();
64   }
65 
66   // The encryption result is cipher text with authentication tag appended.
67   cipher_text->assign(output.begin(),
68                       output.begin() + (output.size() - tag_length_bytes));
69   authentication_tag->assign(output.begin() + cipher_text->size(),
70                              output.end());
71 
72   return Status::Success();
73 }
74 
AesGcmDecrypt(const blink::WebCryptoKey & key,const std::vector<uint8_t> & iv,const std::vector<uint8_t> & additional_data,unsigned int tag_length_bits,const std::vector<uint8_t> & cipher_text,const std::vector<uint8_t> & authentication_tag,std::vector<uint8_t> * plain_text)75 Status AesGcmDecrypt(const blink::WebCryptoKey& key,
76                      const std::vector<uint8_t>& iv,
77                      const std::vector<uint8_t>& additional_data,
78                      unsigned int tag_length_bits,
79                      const std::vector<uint8_t>& cipher_text,
80                      const std::vector<uint8_t>& authentication_tag,
81                      std::vector<uint8_t>* plain_text) {
82   blink::WebCryptoAlgorithm algorithm =
83       CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
84 
85   // Join cipher text and authentication tag.
86   std::vector<uint8_t> cipher_text_with_tag;
87   cipher_text_with_tag.reserve(cipher_text.size() + authentication_tag.size());
88   cipher_text_with_tag.insert(cipher_text_with_tag.end(), cipher_text.begin(),
89                               cipher_text.end());
90   cipher_text_with_tag.insert(cipher_text_with_tag.end(),
91                               authentication_tag.begin(),
92                               authentication_tag.end());
93 
94   return Decrypt(algorithm, key, CryptoData(cipher_text_with_tag), plain_text);
95 }
96 
97 class WebCryptoAesGcmTest : public WebCryptoTestBase {};
98 
TEST_F(WebCryptoAesGcmTest,GenerateKeyBadLength)99 TEST_F(WebCryptoAesGcmTest, GenerateKeyBadLength) {
100   const uint16_t kKeyLen[] = {0, 127, 257};
101   blink::WebCryptoKey key;
102   for (size_t i = 0; i < base::size(kKeyLen); ++i) {
103     SCOPED_TRACE(i);
104     EXPECT_EQ(Status::ErrorGenerateAesKeyLength(),
105               GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true,
106                                 blink::kWebCryptoKeyUsageDecrypt, &key));
107   }
108 }
109 
TEST_F(WebCryptoAesGcmTest,GenerateKeyEmptyUsage)110 TEST_F(WebCryptoAesGcmTest, GenerateKeyEmptyUsage) {
111   blink::WebCryptoKey key;
112   EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(),
113             GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(256), true, 0, &key));
114 }
115 
TEST_F(WebCryptoAesGcmTest,ImportExportJwk)116 TEST_F(WebCryptoAesGcmTest, ImportExportJwk) {
117   const blink::WebCryptoAlgorithm algorithm =
118       CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm);
119 
120   // AES-GCM 128
121   ImportExportJwkSymmetricKey(
122       128, algorithm,
123       blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
124       "A128GCM");
125 
126   // AES-GCM 256
127   ImportExportJwkSymmetricKey(256, algorithm, blink::kWebCryptoKeyUsageDecrypt,
128                               "A256GCM");
129 }
130 
131 // TODO(eroman):
132 //   * Test decryption when the tag length exceeds input size
133 //   * Test decryption with empty input
134 //   * Test decryption with tag length of 0.
TEST_F(WebCryptoAesGcmTest,SampleSets)135 TEST_F(WebCryptoAesGcmTest, SampleSets) {
136   base::ListValue tests;
137   ASSERT_TRUE(ReadJsonTestFileToList("aes_gcm.json", &tests));
138 
139   // Note that WebCrypto appends the authentication tag to the ciphertext.
140   for (size_t test_index = 0; test_index < tests.GetSize(); ++test_index) {
141     SCOPED_TRACE(test_index);
142     base::DictionaryValue* test;
143     ASSERT_TRUE(tests.GetDictionary(test_index, &test));
144 
145     const std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key");
146     const std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv");
147     const std::vector<uint8_t> test_additional_data =
148         GetBytesFromHexString(test, "additional_data");
149     const std::vector<uint8_t> test_plain_text =
150         GetBytesFromHexString(test, "plain_text");
151     const std::vector<uint8_t> test_authentication_tag =
152         GetBytesFromHexString(test, "authentication_tag");
153     const unsigned int test_tag_size_bits =
154         static_cast<unsigned int>(test_authentication_tag.size()) * 8;
155     const std::vector<uint8_t> test_cipher_text =
156         GetBytesFromHexString(test, "cipher_text");
157 
158     blink::WebCryptoKey key = ImportSecretKeyFromRaw(
159         test_key, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm),
160         blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt);
161 
162     // Verify exported raw key is identical to the imported data
163     std::vector<uint8_t> raw_key;
164     EXPECT_EQ(Status::Success(),
165               ExportKey(blink::kWebCryptoKeyFormatRaw, key, &raw_key));
166 
167     EXPECT_BYTES_EQ(test_key, raw_key);
168 
169     // Test encryption.
170     std::vector<uint8_t> cipher_text;
171     std::vector<uint8_t> authentication_tag;
172     EXPECT_EQ(
173         Status::Success(),
174         AesGcmEncrypt(key, test_iv, test_additional_data, test_tag_size_bits,
175                       test_plain_text, &cipher_text, &authentication_tag));
176 
177     EXPECT_BYTES_EQ(test_cipher_text, cipher_text);
178     EXPECT_BYTES_EQ(test_authentication_tag, authentication_tag);
179 
180     // Test decryption.
181     std::vector<uint8_t> plain_text;
182     EXPECT_EQ(
183         Status::Success(),
184         AesGcmDecrypt(key, test_iv, test_additional_data, test_tag_size_bits,
185                       test_cipher_text, test_authentication_tag, &plain_text));
186     EXPECT_BYTES_EQ(test_plain_text, plain_text);
187 
188     // Decryption should fail if any of the inputs are tampered with.
189     EXPECT_EQ(Status::OperationError(),
190               AesGcmDecrypt(key, Corrupted(test_iv), test_additional_data,
191                             test_tag_size_bits, test_cipher_text,
192                             test_authentication_tag, &plain_text));
193     EXPECT_EQ(Status::OperationError(),
194               AesGcmDecrypt(key, test_iv, Corrupted(test_additional_data),
195                             test_tag_size_bits, test_cipher_text,
196                             test_authentication_tag, &plain_text));
197     EXPECT_EQ(Status::OperationError(),
198               AesGcmDecrypt(key, test_iv, test_additional_data,
199                             test_tag_size_bits, Corrupted(test_cipher_text),
200                             test_authentication_tag, &plain_text));
201     EXPECT_EQ(Status::OperationError(),
202               AesGcmDecrypt(key, test_iv, test_additional_data,
203                             test_tag_size_bits, test_cipher_text,
204                             Corrupted(test_authentication_tag), &plain_text));
205 
206     // Try different incorrect tag lengths
207     uint8_t kAlternateTagLengths[] = {0, 8, 96, 120, 128, 160, 255};
208     for (size_t tag_i = 0; tag_i < base::size(kAlternateTagLengths); ++tag_i) {
209       unsigned int wrong_tag_size_bits = kAlternateTagLengths[tag_i];
210       if (test_tag_size_bits == wrong_tag_size_bits)
211         continue;
212       EXPECT_NE(Status::Success(),
213                 AesGcmDecrypt(key, test_iv, test_additional_data,
214                               wrong_tag_size_bits, test_cipher_text,
215                               test_authentication_tag, &plain_text));
216     }
217   }
218 }
219 
220 }  // namespace
221 
222 }  // namespace webcrypto
223