OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_crypto.cpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2021- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#include <iostream>
13
14#include "test_common.hpp"
15
23#include <cstring>
24
25
26static uint8_t testkey[20] = {0x0b, 0x00};
27static uint8_t goodhash[20] = {
28 // clang-format off
29 0x58, 0xea, 0x5a, 0xf0, 0x42, 0x94, 0xe9, 0x17,
30 0xed, 0x84, 0xb9, 0xf0, 0x83, 0x30, 0x23, 0xae,
31 0x8b, 0xa7, 0x7e, 0xb8
32 // clang-format on
33};
34
35static const char *ipsumlorem = "Lorem ipsum dolor sit amet, consectetur "
36 "adipisici elit, sed eiusmod tempor incidunt "
37 "ut labore et dolore magna aliqua.";
38
39TEST(crypto, hmac)
40{
41 uint8_t key[20];
42 std::memcpy(key, testkey, sizeof(key));
43
44 openvpn::SSLLib::CryptoAPI::HMACContext hmac(openvpn::CryptoAlgs::SHA1, key, sizeof(key));
45
46 const uint8_t *ipsum = reinterpret_cast<const uint8_t *>(ipsumlorem);
47
48 hmac.update(ipsum, std::strlen(ipsumlorem));
49 hmac.update(ipsum, std::strlen(ipsumlorem));
50
51 uint8_t hash[20];
52
53 ASSERT_EQ(hmac.final(hash), 20u);
54
55 /* Google test does not seem to have a good memory equality test macro */
56 ASSERT_EQ(std::memcmp(hash, goodhash, sizeof(goodhash)), 0);
57
58 hmac.reset();
59
60 /* Do this again to ensure that reset works */
61 hmac.update(ipsum, std::strlen(ipsumlorem));
62 hmac.update(ipsum, std::strlen(ipsumlorem));
63 ASSERT_EQ(hmac.final(hash), 20u);
64
65 /* Google test does not seem to have a good memory equality test macro */
66 ASSERT_EQ(std::memcmp(hash, goodhash, sizeof(goodhash)), 0);
67
68 /* Overwrite the key to ensure that the memory is no referenced by internal
69 * structs of the hmac */
70 std::memset(key, 0x55, sizeof(key));
71
72 hmac.reset();
73
74 /* Do this again to ensure that reset works */
75 hmac.update(ipsum, std::strlen(ipsumlorem));
76 hmac.update(ipsum, std::strlen(ipsumlorem));
77 ASSERT_EQ(hmac.final(hash), 20u);
78
79 /* Google test does not seem to have a good memory equality test macro */
80 ASSERT_EQ(std::memcmp(hash, goodhash, sizeof(goodhash)), 0);
81}
82
84{
85 const size_t payload = 2048;
86 const size_t headroom = 64;
87 const size_t tailroom = 64;
88 const size_t align_block = 16;
90 return openvpn::Frame::Context{headroom, payload, tailroom, 0, align_block, buffer_flags};
91}
92
93
95{
97 auto frameptr = openvpn::Frame::Ptr{new openvpn::Frame{frame_ctx()}};
99
100
103 dc.set_use_epoch_keys(use_epoch);
104
105 openvpn::SSLLib::Ctx libctx = nullptr;
106 openvpn::CryptoDCFactory::Ptr dc_factory_sel{new openvpn::CryptoDCSelect<openvpn::SSLLib::CryptoAPI>(libctx, frameptr, statsptr, nullptr)};
107
108 auto dc_factory = dc_factory_sel->new_obj(dc);
109
110 cryptodc = dc_factory->new_obj(0);
111
112 uint8_t bigkey[openvpn::OpenVPNStaticKey::KEY_SIZE] = {0};
113
114
115 const uint8_t key[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
116
117 static_assert(sizeof(key) == 32, "Size of key should be 32 bytes");
118
119 /* copy the key a few times to ensure to have the size we need for
120 * Statickey but XOR it to not repeat it */
121
122 for (int i = 0; i < openvpn::OpenVPNStaticKey::KEY_SIZE; i++)
123 {
124 bigkey[i] = static_cast<uint8_t>(key[i % sizeof(key)] ^ i);
125 }
126
127 // Epoch known vector test uses the same key for both e1 send and receive, overwrite s2c cipher key with c2s cipher key
128 std::memcpy(bigkey + 128, bigkey, 64);
129
130 openvpn::OpenVPNStaticKey static_key;
131 std::memcpy(static_key.raw_alloc(), bigkey, sizeof(bigkey));
132
134
135 /* We here make encrypt and decrypt keys the same by design to have the loopback decryption capability */
138
141
142 cryptodc->init_pid("DATA", 0, statsptr);
143
144 return cryptodc;
145}
146
147void test_datachannel_crypto(bool use_epoch)
148{
149 openvpn::CryptoAlgs::allow_default_dc_algs<openvpn::SSLLib::CryptoAPI>(nullptr, true, false);
150
152
153
154 const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
155
157
158 /* reserve some headroom */
159 work.realign(128);
160
161 std::memcpy(work.write_alloc(std::strlen(plaintext)), plaintext, std::strlen(plaintext));
162 const unsigned char *data = work.data();
163 EXPECT_TRUE(std::memcmp(data, plaintext, std::strlen(plaintext)) == 0);
164
165 const std::time_t now = 42;
166
167 const unsigned char op32[]{7, 0, 0, 23};
168
169 bool const wrapwarn = cryptodc->encrypt(work, op32);
170 ASSERT_FALSE(wrapwarn);
171
172 size_t pkt_counter_len = use_epoch ? 8 : 4;
173 size_t tag_len = 16;
174
175 /* 16 for tag, 4 or 8 for packet counter */
176 EXPECT_EQ(work.size(), std::strlen(plaintext) + pkt_counter_len + tag_len);
177
178 const uint8_t exp_tag_short[16]{0x1f, 0xdd, 0x90, 0x8f, 0x0e, 0x9d, 0xc2, 0x5e, 0x79, 0xd8, 0x32, 0x02, 0x0d, 0x58, 0xe7, 0x3f};
179 std::array<uint8_t, 16> exp_tag_epoch = {0Xa0, 0xb5, 0x4c, 0xdd, 0x93, 0xff, 0x0b, 0x01, 0xa3, 0x26, 0x5e, 0xcf, 0x19, 0xd5, 0x6a, 0x06};
180
181 if (use_epoch)
182 {
183 ptrdiff_t tag_offset = 56;
184 uint8_t packetid1[8] = {0, 0x1, 0, 0, 0, 0, 0, 1};
185 EXPECT_EQ(std::memcmp(work.data(), packetid1, 8), 0);
186
187 // Use std::aray for comparison since that gives better gtest output
188
189 std::array<uint8_t, 16> tag{};
190 std::memcpy(tag.data(), work.data() + tag_offset + pkt_counter_len, 16);
191 EXPECT_EQ(tag, exp_tag_epoch);
192
193 // Check a few random bytes of the encrypted output. Different IVs lead to different output here.
194 const uint8_t bytesat14[6]{0x8e, 0x45, 0x5a, 0xdd, 0xd9, 0x0e};
195 EXPECT_EQ(std::memcmp(work.data() + 14, bytesat14, 6), 0);
196 }
197 else
198 {
199 ptrdiff_t tag_offset = 16;
200 uint8_t packetid1[]{0, 0, 0, 1};
201 EXPECT_EQ(std::memcmp(work.data(), packetid1, 4), 0);
202 EXPECT_EQ(std::memcmp(work.data() + pkt_counter_len, exp_tag_short, 16), 0);
203
204 // Check a few random bytes of the encrypted output. Different IVs lead to different output here.
205 const uint8_t bytesat14[6]{0xa8, 0x2e, 0x6b, 0x17, 0x06, 0xd9};
206 EXPECT_EQ(std::memcmp(work.data() + tag_offset + 14, bytesat14, 6), 0);
207 }
208
209 /* Check now if decrypting also works */
210 auto ret = cryptodc->decrypt(work, now, op32);
211
212 EXPECT_EQ(ret, openvpn::Error::SUCCESS);
213 EXPECT_EQ(work.size(), std::strlen(plaintext));
214
215 EXPECT_EQ(std::memcmp(work.data(), plaintext, std::strlen(plaintext)), 0);
216}
217
218TEST(crypto, testEpochIterateKey)
219{
220 openvpn::CryptoAlgs::allow_default_dc_algs<openvpn::SSLLib::CryptoAPI>(nullptr, true, false);
221
224
225 auto *epochdcsend = dynamic_cast<openvpn::AEADEpoch::Crypto<openvpn::SSLLib::CryptoAPI> *>(cryptodcsend.get());
226
227 ASSERT_NE(epochdcsend, nullptr);
228
229 /* Increase the epoch to 4 on the sending you */
230 epochdcsend->increase_send_epoch();
231 epochdcsend->increase_send_epoch();
232 epochdcsend->increase_send_epoch();
233
234 const char *plaintext = "The quick little fox jumps over the bureaucratic hurdles";
235
236 const std::time_t now = 42;
237
238 const unsigned char op32[]{7, 0, 0, 23};
239
241
242 /* reserve some headroom */
243 work.realign(128);
244
245 std::memcpy(work.write_alloc(std::strlen(plaintext)), plaintext, std::strlen(plaintext));
246
247 bool const wrapwarn = cryptodcsend->encrypt(work, op32);
248 ASSERT_FALSE(wrapwarn);
249
250 std::size_t pkt_counter_len = 8;
251 std::size_t tag_len = 16;
252
253 /* 16 for tag, 4 or 8 for packet counter */
254 EXPECT_EQ(work.size(), std::strlen(plaintext) + pkt_counter_len + tag_len);
255
256 std::array<uint8_t, 16> exp_tag_epoch = {0x0f, 0xff, 0xf5, 0x91, 0x3d, 0x39, 0xd7, 0x5b, 0x18, 0x57, 0x3b, 0x57, 0x48, 0x58, 0x9a, 0x7d};
257 ptrdiff_t tag_offset = 56;
258 uint8_t packetid1[8] = {0, 0x4, 0, 0, 0, 0, 0, 1};
259 EXPECT_EQ(std::memcmp(work.data(), packetid1, 8), 0);
260
261 // Use std::aray for comparison since that gives better gtest output
262 std::array<uint8_t, 16> tag{};
263 std::memcpy(tag.data(), work.data() + tag_offset + pkt_counter_len, 16);
264 EXPECT_EQ(tag, exp_tag_epoch);
265
266 // Check a few random bytes of the encrypted output. Different IVs lead to different output here.
267 const uint8_t bytesat14[6]{0x36, 0xaa, 0xb4, 0xd4, 0x9c, 0xe6};
268 EXPECT_EQ(std::memcmp(work.data() + 14, bytesat14, 6), 0);
269
270 /* Check now if decrypting also works */
271 auto ret = cryptodcrecv->decrypt(work, now, op32);
272
273 EXPECT_EQ(ret, openvpn::Error::SUCCESS);
274 EXPECT_EQ(work.size(), std::strlen(plaintext));
275
276 EXPECT_EQ(std::memcmp(work.data(), plaintext, std::strlen(plaintext)), 0);
277}
278
279TEST(crypto, epoch_derive_data_keys)
280{
281 uint8_t epoch_key[32] = {19, 12};
282 openvpn::StaticKey e1{epoch_key, 32};
283
285
286 openvpn::EpochKey epoch{std::move(e1)};
287
288 auto [key, iv] = epoch.data_key(cipher);
289
290 ASSERT_EQ(key.size(), 24);
291 ASSERT_EQ(iv.size(), 12);
292
293 std::array<uint8_t, 24> exp_key{0xed, 0x85, 0x33, 0xdb, 0x1c, 0x28, 0xac, 0xe4, 0x18, 0xe9, 0x00, 0x6a, 0xb2, 0x9c, 0x17, 0x41, 0x7d, 0x60, 0xeb, 0xe6, 0xcd, 0x90, 0xbf, 0x0a};
294
295 std::array<uint8_t, 12> exp_impl_iv{0x86, 0x89, 0x0a, 0xab, 0xf0, 0x32, 0xcb, 0x59, 0xf4, 0xcf, 0xa3, 0x4e};
296
297
298 std::array<uint8_t, 24> key_array;
299 std::array<uint8_t, 12> iv_array;
300
301 std::memcpy(key_array.data(), key.data(), key.size());
302 std::memcpy(iv_array.data(), iv.data(), iv.size());
303
304 EXPECT_EQ(exp_key, key_array);
305 EXPECT_EQ(exp_impl_iv, iv_array);
306}
307
308TEST(crypto, aead_cipher_movable)
309{
310 openvpn::CryptoAlgs::allow_default_dc_algs<openvpn::SSLLib::CryptoAPI>(nullptr, true, false);
311
312 const uint8_t key[32] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '0', '1', '2', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'j', 'k', 'u', 'c', 'h', 'e', 'n', 'l'};
313
314 openvpn::SSLLib::CryptoAPI::CipherContextAEAD cipher;
315 cipher.init(nullptr, openvpn::CryptoAlgs::AES_256_GCM, key, 32, openvpn::SSLLib::CryptoAPI::CipherContextAEAD::ENCRYPT);
316 ASSERT_TRUE(cipher.is_initialized());
317 const uint8_t input[5] = {'h', 'e', 'l', 'l', 'o'};
318 uint8_t encrypted[64] = {0};
319 const uint8_t iv[12] = {0x77};
320 uint8_t *tag = encrypted + sizeof(input);
321
322 cipher.encrypt(input, encrypted, 5, iv, tag, nullptr, 0);
323
324 /* Move constructor */
325 openvpn::SSLLib::CryptoAPI::CipherContextAEAD cipher2(std::move(cipher));
326 ASSERT_TRUE(cipher2.is_initialized());
327 ASSERT_FALSE(cipher.is_initialized());
328
329 uint8_t output2[32] = {0};
330
331 auto ret = cipher2.decrypt(encrypted, output2, 5 + openvpn::SSLLib::CryptoAPI::CipherContextAEAD::AUTH_TAG_LEN, iv, nullptr, nullptr, 0);
332 EXPECT_TRUE(ret);
333
334 ASSERT_EQ(std::memcmp(input, output2, 5), 0);
335
336 /* Move operator= */
337 uint8_t output3[32] = {0};
338
339 openvpn::SSLLib::CryptoAPI::CipherContextAEAD cipher3;
340 cipher3 = std::move(cipher2);
341 ASSERT_TRUE(cipher3.is_initialized());
342 ASSERT_FALSE(cipher2.is_initialized());
343 ASSERT_FALSE(cipher.is_initialized());
344
345 ret = cipher3.decrypt(encrypted, output3, 5 + openvpn::SSLLib::CryptoAPI::CipherContextAEAD::AUTH_TAG_LEN, iv, nullptr, nullptr, 0);
346 EXPECT_TRUE(ret);
347
348 ASSERT_EQ(std::memcmp(input, output3, 5), 0);
349}
350
351TEST(crypto, dcaead_data_v2)
352{
354}
355
356TEST(crypto, dcaead_epoch_data)
357{
358 /* Epoch data needs more refactoring before adjusting the unit test */
360}
361
362TEST(crypto, hkdf_expand_testa1)
363{
364 /* RFC 5889 A.1 Test Case 1 */
365 // clang-format off
366 uint8_t prk[32] =
367 {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
368 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
369 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
370 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5};
371
372 uint8_t info[10] =
373 {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
374 0xf6, 0xf7, 0xf8, 0xf9};
375
376 std::array<uint8_t,42> okm
377 {0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a,
378 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a,
379 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
380 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf,
381 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18,
382 0x58, 0x65};
383 // clang-format on
384
385
386 std::array<uint8_t, 42> out{};
387 openvpn::ovpn_hkdf_expand(prk, info, static_cast<int>(sizeof(info)), out.data(), static_cast<int>(out.size()));
388
389 ASSERT_EQ(out, okm);
390}
391
392TEST(crypto, hkdf_expand_testa2)
393{
394 // clang-format off
395 /* RFC 5889 A.2 Test Case 2 */
396 uint8_t prk[32] =
397 {0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a,
398 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c,
399 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
400 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44};
401
402 uint8_t info[80] =
403 {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
404 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
405 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
406 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
407 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
408 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
409 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
410 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
411 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
412 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
413
414 const int L = 82;
415 std::array<uint8_t,82> okm
416 {0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1,
417 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34,
418 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
419 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c,
420 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72,
421 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09,
422 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8,
423 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71,
424 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87,
425 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f,
426 0x1d, 0x87};
427 // clang-format on
428
429 ASSERT_EQ(L, okm.size());
430 std::array<uint8_t, 82> out = {0xaa};
431 openvpn::ovpn_hkdf_expand(prk, info, static_cast<std::uint16_t>(sizeof(info)), out.data(), static_cast<std::uint16_t>(out.size()));
432
433
434 ASSERT_EQ(out, okm);
435}
436
437TEST(crypto, ovpn_label_expand_test)
438{
439 // clang-format off
440 uint8_t secret[32] =
441 {0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf,
442 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63,
443 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
444 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5};
445
446 std::array<uint8_t, 16> out_expected =
447 {0x18, 0x5e, 0xaa, 0x1c, 0x7f, 0x22, 0x8a, 0xb8,
448 0xeb, 0x29, 0x77, 0x32, 0x14, 0xd9, 0x20, 0x46};
449 // clang-format on
450
451 const uint8_t *label = reinterpret_cast<const uint8_t *>("unit test");
452 std::array<uint8_t, 16> out;
453 openvpn::ovpn_expand_label(secret, sizeof(secret), label, 9, nullptr, 0, out.data(), out.size());
454
455 EXPECT_EQ(out, out_expected);
456}
457
458TEST(crypto, hkdf_expand_testa3)
459{
460 /* RFC 5889 A.3 Test Case 3 */
461 // clang-format off
462 uint8_t prk[32] =
463 {0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16,
464 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf,
465 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77,
466 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04};
467
468 std::array<uint8_t,42> okm =
469 {0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f,
470 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31,
471 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e,
472 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d,
473 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a,
474 0x96, 0xc8};
475 // clang-format off
476
477 uint8_t *info = nullptr;
478 int L = 42;
479
480 std::array<uint8_t,42> out {0xfa};
481 openvpn::ovpn_hkdf_expand(prk, info, 0, out.data(), L);
482
483 ASSERT_EQ(out, okm);
484}
485
486
489{
490public:
491 DataChannelEpochTest(decltype(cipher) cipher, openvpn::StaticKey e1send, openvpn::StaticKey e1recv, uint16_t future_key_count = 16)
492 : openvpn::DataChannelEpoch(cipher, std::move(e1send), std::move(e1recv), nullptr, future_key_count)
493 {
494 }
496
498 {
499 return future_keys.at(i);
500 }
501
506
507
510
513
514
516
517};
518
519class EpochTest : public testing::Test
520{
521protected:
522
523 void SetUp() override
524 {
525 // use 13 as default
526 initDCE(13);
527 }
528
529 void initDCE(uint16_t numfuture)
530 {
531 uint8_t e1send_data[32] = { 0x23 };
532 uint8_t e1recv_data[32] = { 0x27 };
533 openvpn::StaticKey e1send{e1send_data, sizeof (e1send_data)};
534 openvpn::StaticKey e1recv{e1send_data, sizeof (e1recv_data)};
535
536 dce_ = DataChannelEpochTest{openvpn::CryptoAlgs::AES_256_GCM, std::move(e1send), std::move(e1recv), numfuture};
537 }
538
540};
541
542
543TEST_F(EpochTest, key_generation)
544{
545 /* check the keys look like we expect */
546 EXPECT_EQ(dce_.get_future_key(0).epoch, 2);
547 EXPECT_EQ(dce_.get_future_key(12).epoch, 14);
548 EXPECT_EQ(dce_.recv_().epoch, 14);
549 EXPECT_EQ(dce_.send_().epoch, 1);
550}
551
552
553TEST_F(EpochTest, key_rotation)
554{
556 /* should replace send + key recv */
557 dce_.replace_update_recv_key(9, stats);
558
559 EXPECT_EQ(dce_.recv_ctx_().epoch, 9);
560 EXPECT_EQ(dce_.send_ctx_().epoch, 9);
561 EXPECT_EQ(dce_.retire_().epoch, 1);
562
563
564 /* Iterate the data send key four times to get it to 13 */
565 for (int i = 0; i < 4; i++)
566 {
567 dce_.iterate_send_key();
568 }
569
570 EXPECT_EQ(dce_.send_ctx_().epoch, 13);
571EXPECT_EQ(dce_.send_().epoch, 13);
572
573 /* recv context should still be 9 */
574EXPECT_EQ(dce_.recv_ctx_().epoch, 9);
575
576 dce_.replace_update_recv_key(10, stats);
577
578EXPECT_EQ(dce_.recv_ctx_().epoch, 10);
579EXPECT_EQ(dce_.send_ctx_().epoch, 13);
580 EXPECT_EQ(dce_.send_().epoch, 13);
581
582EXPECT_EQ(dce_.retire_().epoch, 9);
583
584dce_.replace_update_recv_key(12, stats);
585EXPECT_EQ(dce_.recv_ctx_().epoch, 12);
586EXPECT_EQ(dce_.send_ctx_().epoch, 13);
587EXPECT_EQ(dce_.send_().epoch, 13);
588
589EXPECT_EQ(dce_.retire_().epoch, 10);
590
591dce_.iterate_send_key();
592EXPECT_EQ(dce_.send_ctx_().epoch, 14);
593}
594
595TEST_F(EpochTest, key_receive_lookup)
596{
598
599 /* lookup some wacky things that should fail */
600 EXPECT_EQ(dce_.lookup_decrypt_key(2000), nullptr);
601 EXPECT_EQ(dce_.lookup_decrypt_key(-1), nullptr);
602 EXPECT_EQ(dce_.lookup_decrypt_key(0xefff), nullptr);
603
604 /* Lookup the edges of the current window */
605 EXPECT_EQ(dce_.lookup_decrypt_key(0), nullptr);
606 EXPECT_EQ(dce_.lookup_decrypt_key(1)->epoch, 1);
607 EXPECT_EQ(dce_.lookup_decrypt_key(2)->epoch, 2);
608 EXPECT_EQ(dce_.lookup_decrypt_key(13)->epoch, 13);
609 EXPECT_EQ(dce_.lookup_decrypt_key(14)->epoch, 14);
610 EXPECT_EQ(dce_.lookup_decrypt_key(15), nullptr);
611
612 /* Should move 1 to retiring key but leave 1-5 undefined, 7 as
613 * active and 8-20 as future keys*/
614 dce_.replace_update_recv_key(7, stats);
615
616 EXPECT_EQ(dce_.lookup_decrypt_key(0), nullptr);
617 EXPECT_EQ(dce_.lookup_decrypt_key(1)->epoch, 1);
618 EXPECT_EQ(dce_.lookup_decrypt_key(2), nullptr);
619 EXPECT_EQ(dce_.lookup_decrypt_key(3), nullptr);
620 EXPECT_EQ(dce_.lookup_decrypt_key(4), nullptr);
621 EXPECT_EQ(dce_.lookup_decrypt_key(5), nullptr);
622 EXPECT_EQ(dce_.lookup_decrypt_key(6), nullptr);
623 EXPECT_EQ(dce_.lookup_decrypt_key( 7)->epoch, 7);
624 EXPECT_EQ(dce_.lookup_decrypt_key( 8)->epoch, 8);
625 EXPECT_EQ(dce_.lookup_decrypt_key( 20)->epoch, 20);
626 EXPECT_EQ(dce_.lookup_decrypt_key(21), nullptr);
627 EXPECT_EQ(dce_.lookup_decrypt_key(22), nullptr);
628
629
630 /* Should move 7 to retiring key and have 8 as active key and
631 * 9-21 as future keys */
632dce_.replace_update_recv_key(8, stats);
633 EXPECT_EQ(dce_.lookup_decrypt_key(0), nullptr);
634 EXPECT_EQ(dce_.lookup_decrypt_key(1), nullptr);
635 EXPECT_EQ(dce_.lookup_decrypt_key(2), nullptr);
636 EXPECT_EQ(dce_.lookup_decrypt_key(3), nullptr);
637 EXPECT_EQ(dce_.lookup_decrypt_key(4), nullptr);
638 EXPECT_EQ(dce_.lookup_decrypt_key(5), nullptr);
639 EXPECT_EQ(dce_.lookup_decrypt_key(6), nullptr);
640 EXPECT_EQ(dce_.lookup_decrypt_key( 7)->epoch, 7);
641 EXPECT_EQ(dce_.lookup_decrypt_key( 8)->epoch, 8);
642 EXPECT_EQ(dce_.lookup_decrypt_key( 20)->epoch, 20);
643 EXPECT_EQ(dce_.lookup_decrypt_key( 21)->epoch, 21);
644 EXPECT_EQ(dce_.lookup_decrypt_key(22), nullptr);
645 EXPECT_EQ(dce_.lookup_decrypt_key(23), nullptr);
646}
647
648TEST_F(EpochTest, key_overflow)
649{
651 initDCE(32);
652
653 /* Modify the receive epoch and keys to have a very high epoch to test
654 * the end of array. Iterating through all 16k keys takes a 2-3s, so we
655 * avoid this for the unit test */
656 dce_.recv_ctx_().epoch = 16000;
657 dce_.send_ctx_().epoch = 16000;
658
659 dce_.send_().epoch = 16000;
660 dce_.recv_().epoch = 16000 + dce_.get_future_keys_count();
661
662 for (uint16_t i = 0; i < dce_.get_future_keys_count(); i++)
663 {
664 dce_.get_future_key(i).epoch = 16001 + i;
665 }
666
667 /* Move the last few keys until we are close to the limit */
668 while (dce_.recv_ctx_().epoch < (UINT16_MAX - 40))
669 {
670 dce_.replace_update_recv_key(dce_.recv_ctx_().epoch + 10, stats);
671 }
672
673 /* Looking up this key should still work as it will not break the limit
674 * when generating keys */
675 EXPECT_EQ(dce_.lookup_decrypt_key( UINT16_MAX - 34)->epoch, UINT16_MAX - 34);
676 EXPECT_EQ(dce_.lookup_decrypt_key( UINT16_MAX - 33)->epoch, UINT16_MAX - 33);
677
678 /* This key is no longer eligible for decrypting as the 13 future keys
679 * would be larger than uint16_t maximum */
680 EXPECT_EQ(dce_.lookup_decrypt_key(UINT16_MAX - 32), nullptr);
681 EXPECT_EQ(dce_.lookup_decrypt_key(UINT16_MAX), nullptr);
682
683 /* Check that moving to the last possible epoch works */
684 dce_.replace_update_recv_key(UINT16_MAX - 33, stats);
685 EXPECT_EQ(dce_.lookup_decrypt_key( UINT16_MAX - 33)->epoch, UINT16_MAX - 33);
686 EXPECT_EQ(dce_.lookup_decrypt_key(UINT16_MAX - 32), nullptr);
687 EXPECT_EQ(dce_.lookup_decrypt_key(UINT16_MAX), nullptr);
688}
openvpn::EpochKey & recv_()
DataChannelEpochTest()=default
DataChannelEpochTest(decltype(cipher) cipher, openvpn::StaticKey e1send, openvpn::StaticKey e1recv, uint16_t future_key_count=16)
openvpn::EpochDataChannelCryptoContext & recv_ctx_()
openvpn::EpochDataChannelCryptoContext & get_future_key(decltype(future_keys)::size_type i)
openvpn::EpochDataChannelCryptoContext & retire_()
openvpn::EpochKey & send_()
openvpn::EpochDataChannelCryptoContext & send_ctx_()
DataChannelEpochTest dce_
void initDCE(uint16_t numfuture)
void SetUp() override
BufferAllocatedType & realign(const size_t headroom)
Realign the buffer with the specified headroom.
Definition buffer.hpp:1760
virtual void init_pid(const char *recv_name, const int recv_unit, const SessionStats::Ptr &recv_stats_arg)=0
virtual void init_hmac(StaticKey &&encrypt_key, StaticKey &&decrypt_key)=0
virtual bool encrypt(BufferAllocated &buf, const unsigned char *op32)=0
virtual Error::Type decrypt(BufferAllocated &buf, std::time_t now, const unsigned char *op32)=0
virtual void init_cipher(StaticKey &&encrypt_key, StaticKey &&decrypt_key)=0
CryptoDCContext::Ptr new_obj(CryptoDCSettingsData dc_settings) override
void set_cipher(CryptoAlgs::Type cipher)
Definition cryptodc.hpp:105
void set_use_epoch_keys(bool use_epoch)
Definition cryptodc.hpp:115
EpochDataChannelDecryptContext retiring_decrypt_ctx
EpochDataChannelEncryptContext encrypt_ctx
EpochDataChannelDecryptContext decrypt_ctx
std::vector< EpochDataChannelDecryptContext > future_keys
openvpn::CryptoAlgs::Type cipher
std::pair< StaticKey, StaticKey > data_key(openvpn::CryptoAlgs::Type cipher)
unsigned char * raw_alloc()
StaticKey slice(unsigned int key_specifier) const
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
void work(openvpn_io::io_context &io_context, ThreadCommon &tc, MyRunContext &runctx, const unsigned int unit)
constexpr BufferFlags NO_FLAGS(0u)
no flags set
void ovpn_expand_label(const uint8_t *secret, size_t secret_len, const uint8_t *label, size_t label_len, const uint8_t *context, size_t context_len, uint8_t *out, size_t out_len)
void ovpn_hkdf_expand(const uint8_t *secret, const uint8_t *info, int info_len, uint8_t *out, int out_len)
std::string ret
openvpn::CryptoDCInstance::Ptr create_dctest_instance(bool use_epoch)
static openvpn::Frame::Context frame_ctx()
static uint8_t testkey[20]
TEST(crypto, hmac)
static uint8_t goodhash[20]
TEST_F(EpochTest, key_generation)
void test_datachannel_crypto(bool use_epoch)
static const char * ipsumlorem
static const char * input[]
static std::stringstream out
Definition test_path.cpp:10