xref: /aosp_15_r20/external/tink/cc/subtle/xchacha20_poly1305_boringssl_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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