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