1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ////////////////////////////////////////////////////////////////////////////////
16
17 #include "tink/subtle/xchacha20_poly1305_boringssl.h"
18
19 #include <memory>
20 #include <string>
21 #include <vector>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/status/status.h"
26 #include "absl/strings/escaping.h"
27 #include "absl/strings/str_cat.h"
28 #include "tink/aead/internal/wycheproof_aead.h"
29 #include "tink/config/tink_fips.h"
30 #include "tink/internal/ssl_util.h"
31 #include "tink/subtle/subtle_util.h"
32 #include "tink/util/secret_data.h"
33 #include "tink/util/status.h"
34 #include "tink/util/statusor.h"
35 #include "tink/util/test_matchers.h"
36
37 namespace crypto {
38 namespace tink {
39 namespace subtle {
40 namespace {
41
42 constexpr int kNonceSizeInBytes = 24;
43 constexpr int kTagSizeInBytes = 16;
44
45 constexpr absl::string_view kKey256Hex =
46 "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
47 constexpr absl::string_view kMessage = "Some data to encrypt.";
48 constexpr absl::string_view kAssociatedData = "Some data to authenticate.";
49
50 using ::crypto::tink::test::IsOk;
51 using ::crypto::tink::test::StatusIs;
52 using ::testing::AllOf;
53 using ::testing::Eq;
54 using ::testing::Not;
55 using ::testing::SizeIs;
56 using ::testing::TestWithParam;
57 using ::testing::ValuesIn;
58
TEST(XChacha20Poly1305BoringSslTest,EncryptDecrypt)59 TEST(XChacha20Poly1305BoringSslTest, EncryptDecrypt) {
60 if (IsFipsModeEnabled()) {
61 GTEST_SKIP() << "Not supported in FIPS-only mode";
62 }
63
64 util::SecretData key =
65 util::SecretDataFromStringView(absl::HexStringToBytes(kKey256Hex));
66 if (!internal::IsBoringSsl()) {
67 EXPECT_THAT(XChacha20Poly1305BoringSsl::New(key).status(),
68 StatusIs(absl::StatusCode::kUnimplemented));
69 } else {
70 util::StatusOr<std::unique_ptr<Aead>> aead =
71 XChacha20Poly1305BoringSsl::New(key);
72 ASSERT_THAT(aead, IsOk());
73
74 util::StatusOr<std::string> ciphertext =
75 (*aead)->Encrypt(kMessage, kAssociatedData);
76 ASSERT_THAT(ciphertext, IsOk());
77 EXPECT_THAT(*ciphertext,
78 SizeIs(kMessage.size() + kNonceSizeInBytes + kTagSizeInBytes));
79 util::StatusOr<std::string> plaintext =
80 (*aead)->Decrypt(*ciphertext, kAssociatedData);
81 ASSERT_THAT(plaintext, IsOk());
82 EXPECT_EQ(*plaintext, kMessage);
83 }
84 }
85
86 // Test decryption with a known ciphertext, message, associated_data and key
87 // tuple to make sure this is using the correct algorithm. The values are taken
88 // from the test vector tcId 1 of the Wycheproof tests:
89 // https://github.com/google/wycheproof/blob/master/testvectors/xchacha20_poly1305_test.json#L21
TEST(XChacha20Poly1305BoringSslTest,SimpleDecrypt)90 TEST(XChacha20Poly1305BoringSslTest, SimpleDecrypt) {
91 if (!internal::IsBoringSsl()) {
92 GTEST_SKIP() << "Unimplemented with OpenSSL";
93 }
94 if (IsFipsModeEnabled()) {
95 GTEST_SKIP() << "Not supported in FIPS-only mode";
96 }
97
98 std::string message = absl::HexStringToBytes(
99 "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66"
100 "202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e6520"
101 "74697020666f7220746865206675747572652c2073756e73637265656e20776f756c6420"
102 "62652069742e");
103 std::string raw_ciphertext = absl::HexStringToBytes(
104 "bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb731c7f1b"
105 "0b4aa6440bf3a82f4eda7e39ae64c6708c54c216cb96b72e1213b4522f8c9ba40db5d945"
106 "b11b69b982c1bb9e3f3fac2bc369488f76b2383565d3fff921f9664c97637da9768812f6"
107 "15c68b13b52e");
108 std::string iv = absl::HexStringToBytes(
109 "404142434445464748494a4b4c4d4e4f5051525354555657");
110 std::string tag = absl::HexStringToBytes("c0875924c1c7987947deafd8780acf49");
111 std::string associated_data =
112 absl::HexStringToBytes("50515253c0c1c2c3c4c5c6c7");
113 util::SecretData key = util::SecretDataFromStringView(absl::HexStringToBytes(
114 "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"));
115
116 util::StatusOr<std::unique_ptr<Aead>> aead =
117 XChacha20Poly1305BoringSsl::New(key);
118 ASSERT_THAT(aead, IsOk());
119
120 util::StatusOr<std::string> plaintext =
121 (*aead)->Decrypt(absl::StrCat(iv, raw_ciphertext, tag), associated_data);
122 ASSERT_THAT(plaintext, IsOk());
123 EXPECT_EQ(*plaintext, message);
124 }
125
TEST(XChacha20Poly1305BoringSslTest,DecryptFailsIfCiphertextTooSmall)126 TEST(XChacha20Poly1305BoringSslTest, DecryptFailsIfCiphertextTooSmall) {
127 if (!internal::IsBoringSsl()) {
128 GTEST_SKIP() << "Unimplemented with OpenSSL";
129 }
130 if (IsFipsModeEnabled()) {
131 GTEST_SKIP() << "Not supported in FIPS-only mode";
132 }
133
134 util::SecretData key =
135 util::SecretDataFromStringView(absl::HexStringToBytes(kKey256Hex));
136 util::StatusOr<std::unique_ptr<Aead>> aead =
137 XChacha20Poly1305BoringSsl::New(key);
138 ASSERT_THAT(aead, IsOk());
139
140 for (int i = 1; i < kNonceSizeInBytes + kTagSizeInBytes; i++) {
141 std::string ciphertext;
142 ResizeStringUninitialized(&ciphertext, i);
143 EXPECT_THAT((*aead)->Decrypt(ciphertext, kAssociatedData).status(),
144 StatusIs(absl::StatusCode::kInvalidArgument));
145 }
146 }
147
TEST(XChacha20Poly1305BoringSslTest,FailisOnFipsOnlyMode)148 TEST(XChacha20Poly1305BoringSslTest, FailisOnFipsOnlyMode) {
149 if (!internal::IsBoringSsl()) {
150 GTEST_SKIP() << "Unimplemented with OpenSSL";
151 }
152 if (!IsFipsModeEnabled()) {
153 GTEST_SKIP() << "Only ran in in FIPS-only mode";
154 }
155
156 util::SecretData key256 =
157 util::SecretDataFromStringView(absl::HexStringToBytes(kKey256Hex));
158
159 EXPECT_THAT(XChacha20Poly1305BoringSsl::New(key256).status(),
160 StatusIs(absl::StatusCode::kInternal));
161 }
162
163 class XChacha20Poly1305BoringSslWycheproofTest
164 : public TestWithParam<internal::WycheproofTestVector> {
SetUp()165 void SetUp() override {
166 if (!internal::IsBoringSsl()) {
167 GTEST_SKIP() << "Unimplemented with OpenSSL";
168 }
169 if (IsFipsModeEnabled()) {
170 GTEST_SKIP() << "Not supported in FIPS-only mode";
171 }
172 internal::WycheproofTestVector test_vector = GetParam();
173 if (test_vector.key.size() != 32 ||
174 test_vector.nonce.size() != kNonceSizeInBytes ||
175 test_vector.tag.size() != kTagSizeInBytes) {
176 GTEST_SKIP() << "Unsupported parameters: key size "
177 << test_vector.key.size()
178 << " nonce size: " << test_vector.nonce.size()
179 << " tag size: " << test_vector.tag.size();
180 }
181 }
182 };
183
TEST_P(XChacha20Poly1305BoringSslWycheproofTest,Decrypt)184 TEST_P(XChacha20Poly1305BoringSslWycheproofTest, Decrypt) {
185 internal::WycheproofTestVector test_vector = GetParam();
186 util::SecretData key = util::SecretDataFromStringView(test_vector.key);
187 util::StatusOr<std::unique_ptr<Aead>> cipher =
188 XChacha20Poly1305BoringSsl::New(key);
189 ASSERT_THAT(cipher, IsOk());
190 std::string ciphertext =
191 absl::StrCat(test_vector.nonce, test_vector.ct, test_vector.tag);
192 util::StatusOr<std::string> plaintext =
193 (*cipher)->Decrypt(ciphertext, test_vector.aad);
194 if (plaintext.ok()) {
195 EXPECT_NE(test_vector.expected, "invalid")
196 << "Decrypted invalid ciphertext with ID " << test_vector.id;
197 EXPECT_EQ(*plaintext, test_vector.msg)
198 << "Incorrect decryption: " << test_vector.id;
199 } else {
200 EXPECT_THAT(test_vector.expected, Not(AllOf(Eq("valid"), Eq("acceptable"))))
201 << "Could not decrypt test with tcId: " << test_vector.id
202 << " iv_size: " << test_vector.nonce.size()
203 << " tag_size: " << test_vector.tag.size()
204 << " key_size: " << key.size() << "; error: " << plaintext.status();
205 }
206 }
207
208 INSTANTIATE_TEST_SUITE_P(XChacha20Poly1305BoringSslWycheproofTests,
209 XChacha20Poly1305BoringSslWycheproofTest,
210 ValuesIn(internal::ReadWycheproofTestVectors(
211 /*file_name=*/"xchacha20_poly1305_test.json")));
212
213 } // namespace
214 } // namespace subtle
215 } // namespace tink
216 } // namespace crypto
217