1 // Copyright 2020 Google LLC
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/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_sender_kem_boringssl.h"
18 
19 #include <string>
20 #include <utility>
21 
22 #include "gtest/gtest.h"
23 #include "absl/memory/memory.h"
24 #include "absl/status/status.h"
25 #include "openssl/curve25519.h"
26 #include "openssl/hrss.h"
27 #include "openssl/sha.h"
28 #include "tink/config/tink_fips.h"
29 #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_hkdf_recipient_kem_boringssl.h"
30 #include "tink/experimental/pqcrypto/kem/subtle/cecpq2_subtle_boringssl_util.h"
31 #include "tink/subtle/common_enums.h"
32 #include "tink/subtle/hkdf.h"
33 #include "tink/subtle/random.h"
34 #include "tink/subtle/subtle_util.h"
35 #include "tink/util/secret_data.h"
36 #include "tink/util/status.h"
37 #include "tink/util/statusor.h"
38 #include "tink/util/test_matchers.h"
39 #include "tink/util/test_util.h"
40 
41 using ::crypto::tink::test::IsOk;
42 using ::crypto::tink::test::StatusIs;
43 using ::testing::HasSubstr;
44 
45 namespace crypto {
46 namespace tink {
47 namespace subtle {
48 namespace {
49 
50 // This test evaluates the creation of a Cecpq2HkdfSenderKemBoringSsl instance
51 // with an unknown curve type parameter. It should fail with an
52 // absl::StatusCode::kUnimplemented error.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestUnknownCurve)53 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestUnknownCurve) {
54   if (IsFipsModeEnabled()) {
55     GTEST_SKIP() << "Not supported in FIPS-only mode";
56   }
57 
58   auto status_or_cecpq2_key =
59       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
60   ASSERT_TRUE(status_or_cecpq2_key.ok());
61   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
62 
63   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl specifying an unknown
64   // curve
65   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
66       EllipticCurveType::UNKNOWN_CURVE, cecpq2_key_pair.x25519_key_pair.pub_x,
67       cecpq2_key_pair.x25519_key_pair.pub_y,
68       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
69 
70   // The instance creation above should fail with an unimplemented algorithm
71   // error given the UNKNOWN_CURVE parameter
72   EXPECT_EQ(absl::StatusCode::kUnimplemented,
73             status_or_sender_kem.status().code());
74 }
75 
76 // This test evaluates the case where an unsupported curve (NIST_P256) is
77 // specified. This test should fail with an absl::StatusCode::kUnimplemented
78 // error.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestUnsupportedCurve)79 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestUnsupportedCurve) {
80   if (IsFipsModeEnabled()) {
81     GTEST_SKIP() << "Not supported in FIPS-only mode";
82   }
83 
84   auto status_or_cecpq2_key =
85       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
86   ASSERT_TRUE(status_or_cecpq2_key.ok());
87   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
88 
89   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl specifying a
90   // unsupported curve
91   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
92       EllipticCurveType::NIST_P256, cecpq2_key_pair.x25519_key_pair.pub_x,
93       cecpq2_key_pair.x25519_key_pair.pub_y,
94       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
95 
96   // This test should fail with an unimplemented algorithm error
97   EXPECT_EQ(absl::StatusCode::kUnimplemented,
98             status_or_sender_kem.status().code());
99 }
100 
101 // This test checks that an error is triggered if an output key lenth smaller
102 // than 32 bytes is specified.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestNotPostQuantumSecureKeyLength)103 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestNotPostQuantumSecureKeyLength) {
104   if (IsFipsModeEnabled()) {
105     GTEST_SKIP() << "Not supported in FIPS-only mode";
106   }
107 
108   // Declaring auxiliary parameters
109   std::string salt_hex = "0b0b0b0b";
110   std::string info_hex = "0b0b0b0b0b0b0b0b";
111 
112   // Not post-quantum secure output key length
113   int out_len = 31;
114 
115   auto status_or_cecpq2_key =
116       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
117   ASSERT_TRUE(status_or_cecpq2_key.ok());
118   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
119 
120   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl
121   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
122       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.pub_x,
123       cecpq2_key_pair.x25519_key_pair.pub_y,
124       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
125   ASSERT_THAT(status_or_sender_kem, IsOk());
126   auto sender_kem = std::move(status_or_sender_kem.value());
127 
128   // Generating a symmetric key
129   auto status_or_kem_key = sender_kem->GenerateKey(
130       HashType::SHA256, test::HexDecodeOrDie(salt_hex),
131       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
132 
133   EXPECT_THAT(status_or_kem_key.status(),
134               StatusIs(absl::StatusCode::kInvalidArgument,
135                        HasSubstr("not post-quantum secure")));
136 }
137 
138 // This test evaluates if a Sender can successfully generate a symmetric key.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestGenerateKey)139 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestGenerateKey) {
140   if (IsFipsModeEnabled()) {
141     GTEST_SKIP() << "Not supported in FIPS-only mode";
142   }
143 
144   // Declaring auxiliary parameters
145   std::string salt_hex = "0b0b0b0b";
146   std::string info_hex = "0b0b0b0b0b0b0b0b";
147   int out_len = 32;
148 
149   auto status_or_cecpq2_key =
150       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
151   ASSERT_TRUE(status_or_cecpq2_key.ok());
152   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
153 
154   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl
155   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
156       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.pub_x,
157       cecpq2_key_pair.x25519_key_pair.pub_y,
158       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
159   ASSERT_THAT(status_or_sender_kem, IsOk());
160   auto sender_kem = std::move(status_or_sender_kem.value());
161 
162   // Generating a symmetric key
163   auto status_or_kem_key = sender_kem->GenerateKey(
164       HashType::SHA256, test::HexDecodeOrDie(salt_hex),
165       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
166 
167   // Asserting that the symmetric key has been successfully generated
168   ASSERT_THAT(status_or_kem_key, IsOk());
169   auto kem_key = std::move(status_or_kem_key.value());
170   EXPECT_FALSE(kem_key->get_kem_bytes().empty());
171   EXPECT_EQ(kem_key->get_symmetric_key().size(), out_len);
172 }
173 
174 // This test evaluates the whole KEM flow: from Sender to Recipient. This test
175 // should successfully generate an encapsulated shared secret that matches with
176 // a decapsulated shared secret.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestSenderRecipientFullFlowSuccess)177 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestSenderRecipientFullFlowSuccess) {
178   if (IsFipsModeEnabled()) {
179     GTEST_SKIP() << "Not supported in FIPS-only mode";
180   }
181 
182   // Declaring auxiliary parameters
183   std::string salt_hex = "0b0b0b0b";
184   std::string info_hex = "0b0b0b0b0b0b0b0b";
185   int out_len = 32;
186 
187   auto status_or_cecpq2_key =
188       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
189   ASSERT_TRUE(status_or_cecpq2_key.ok());
190   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
191 
192   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl
193   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
194       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.pub_x,
195       cecpq2_key_pair.x25519_key_pair.pub_y,
196       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
197   ASSERT_TRUE(status_or_sender_kem.ok());
198   auto sender_kem = std::move(status_or_sender_kem.value());
199 
200   // Generating sender's shared secret
201   auto status_or_kem_key = sender_kem->GenerateKey(
202       HashType::SHA256, test::HexDecodeOrDie(salt_hex),
203       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
204   ASSERT_TRUE(status_or_kem_key.ok());
205   auto kem_key = std::move(status_or_kem_key.value());
206 
207   // Initializing recipient's KEM data structure using recipient's private keys
208   auto status_or_recipient_kem = Cecpq2HkdfRecipientKemBoringSsl::New(
209       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.priv,
210       std::move(cecpq2_key_pair.hrss_key_pair.hrss_private_key_seed));
211   ASSERT_TRUE(status_or_recipient_kem.ok());
212   auto recipient_kem = std::move(status_or_recipient_kem.value());
213 
214   // Generating recipient's shared secret
215   auto status_or_shared_secret = recipient_kem->GenerateKey(
216       kem_key->get_kem_bytes(), HashType::SHA256,
217       test::HexDecodeOrDie(salt_hex), test::HexDecodeOrDie(info_hex), out_len,
218       EcPointFormat::COMPRESSED);
219   ASSERT_TRUE(status_or_shared_secret.ok());
220 
221   // Asserting that both shared secrets match
222   EXPECT_EQ(test::HexEncode(
223                 util::SecretDataAsStringView(kem_key->get_symmetric_key())),
224             test::HexEncode(
225                 util::SecretDataAsStringView(status_or_shared_secret.value())));
226 }
227 
228 // This test evaluates the whole KEM flow as in
229 // TestSenderRecipientFullFlowSuccess with the difference that the caller's
230 // public key is erased after Cecpq2HkdfSenderKemBoringSsl object is created.
231 // This test would detect if the caller's public key buffers are being used
232 // by Cecpq2HkdfSenderKemBoringSsl instead of Cecpq2HkdfSenderKemBoringSsl
233 // having its own explicit copy.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestFullFlowErasedCallersPublicKey)234 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestFullFlowErasedCallersPublicKey) {
235   if (IsFipsModeEnabled()) {
236     GTEST_SKIP() << "Not supported in FIPS-only mode";
237   }
238 
239   // Declaring auxiliary parameters
240   std::string salt_hex = "0b0b0b0b";
241   std::string info_hex = "0b0b0b0b0b0b0b0b";
242   int out_len = 32;
243 
244   auto status_or_cecpq2_key =
245       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
246   ASSERT_TRUE(status_or_cecpq2_key.ok());
247   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
248 
249   // Creating an instance of Cecpq2HkdfSenderKemBoringSsl
250   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
251       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.pub_x,
252       cecpq2_key_pair.x25519_key_pair.pub_y,
253       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
254   ASSERT_THAT(status_or_sender_kem, IsOk());
255   auto sender_kem = std::move(status_or_sender_kem.value());
256 
257   // Erasing caller's public key buffers
258   cecpq2_key_pair.x25519_key_pair.pub_x.clear();
259   cecpq2_key_pair.x25519_key_pair.pub_y.clear();
260   cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled.clear();
261 
262   // Generating sender's shared secret
263   auto status_or_kem_key = sender_kem->GenerateKey(
264       HashType::SHA256, test::HexDecodeOrDie(salt_hex),
265       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
266   ASSERT_THAT(status_or_kem_key, IsOk());
267   auto kem_key = std::move(status_or_kem_key.value());
268 
269   // Initializing recipient's KEM data structure using recipient's private keys
270   auto status_or_recipient_kem = Cecpq2HkdfRecipientKemBoringSsl::New(
271       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.priv,
272       std::move(cecpq2_key_pair.hrss_key_pair.hrss_private_key_seed));
273   ASSERT_THAT(status_or_recipient_kem, IsOk());
274   auto recipient_kem = std::move(status_or_recipient_kem.value());
275 
276   // Generating recipient's shared secret
277   auto status_or_shared_secret = recipient_kem->GenerateKey(
278       kem_key->get_kem_bytes(), HashType::SHA256,
279       test::HexDecodeOrDie(salt_hex), test::HexDecodeOrDie(info_hex), out_len,
280       EcPointFormat::COMPRESSED);
281   ASSERT_THAT(status_or_shared_secret, IsOk());
282 
283   // Asserting that both shared secrets match
284   EXPECT_EQ(test::HexEncode(
285                 util::SecretDataAsStringView(kem_key->get_symmetric_key())),
286             test::HexEncode(
287                 util::SecretDataAsStringView(status_or_shared_secret.value())));
288 }
289 
290 // This test evaluates the whole KEM flow: from Sender to Recipient. This test
291 // is essentially the same as TestSenderRecipientFullFlowSuccess with the
292 // difference that we alter bytes of the kem_bytes thus preventing the two
293 // shared secrets to match.
TEST(Cecpq2HkdfSenderKemBoringSslTest,TestSenderRecipientFullFlowFailure)294 TEST(Cecpq2HkdfSenderKemBoringSslTest, TestSenderRecipientFullFlowFailure) {
295   if (IsFipsModeEnabled()) {
296     GTEST_SKIP() << "Not supported in FIPS-only mode";
297   }
298 
299   // Declaring auxiliary parameters
300   std::string info_hex = "0b0b0b0b0b0b0b0b";
301   std::string salt_hex = "0b0b0b0b";
302   int out_len = 32;
303 
304   auto status_or_cecpq2_key =
305       pqc::GenerateCecpq2Keypair(EllipticCurveType::CURVE25519);
306   ASSERT_TRUE(status_or_cecpq2_key.ok());
307   auto cecpq2_key_pair = std::move(status_or_cecpq2_key).value();
308 
309   // Initializing sender's KEM data structure using recipient's public keys
310   auto status_or_sender_kem = Cecpq2HkdfSenderKemBoringSsl::New(
311       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.pub_x,
312       cecpq2_key_pair.x25519_key_pair.pub_y,
313       cecpq2_key_pair.hrss_key_pair.hrss_public_key_marshaled);
314   ASSERT_THAT(status_or_sender_kem, IsOk());
315   auto sender_kem = std::move(status_or_sender_kem.value());
316 
317   // Generating sender's shared secret (using salt_hex1)
318   auto status_or_kem_key = sender_kem->GenerateKey(
319       HashType::SHA256, test::HexDecodeOrDie(salt_hex),
320       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
321   ASSERT_THAT(status_or_kem_key, IsOk());
322   auto kem_key = std::move(status_or_kem_key.value());
323 
324   // Initializing recipient's KEM data structure using recipient's private keys
325   auto status_or_recipient_kem = Cecpq2HkdfRecipientKemBoringSsl::New(
326       EllipticCurveType::CURVE25519, cecpq2_key_pair.x25519_key_pair.priv,
327       std::move(cecpq2_key_pair.hrss_key_pair.hrss_private_key_seed));
328   ASSERT_THAT(status_or_recipient_kem, IsOk());
329   auto recipient_kem = std::move(status_or_recipient_kem.value());
330 
331   // Here, we corrupt kem_bytes (we change all bytes to "a") so that
332   // the HRSS shared secret is not successfully recovered
333   std::string kem_bytes = kem_key->get_kem_bytes();
334   for (int i = 0; i < HRSS_CIPHERTEXT_BYTES; i++)
335     kem_bytes[X25519_PUBLIC_VALUE_LEN + i] = 'a';
336 
337   // Generating the defective recipient's shared secret
338   auto status_or_shared_secret = recipient_kem->GenerateKey(
339       kem_bytes, HashType::SHA256, test::HexDecodeOrDie(salt_hex),
340       test::HexDecodeOrDie(info_hex), out_len, EcPointFormat::COMPRESSED);
341 
342   // With very high probability, the shared secrets should not match
343   EXPECT_NE(test::HexEncode(
344                 util::SecretDataAsStringView(kem_key->get_symmetric_key())),
345             test::HexEncode(
346                 util::SecretDataAsStringView(status_or_shared_secret.value())));
347 }
348 
349 }  // namespace
350 }  // namespace subtle
351 }  // namespace tink
352 }  // namespace crypto
353