/* SPDX-License-Identifier: GPL-3.0-or-later * Copyright © 2026 The TokTok team. */ #include "shared_key_cache.h" #include #include #include #include #include "../testing/support/public/simulation.hh" #include "attributes.h" #include "mono_time.h" namespace { using tox::test::SimulatedNode; using tox::test::Simulation; class SharedKeyCacheTest : public ::testing::Test { protected: void SetUp() override { sim = std::make_unique(); node = sim->create_node(); crypto_new_keypair(&node->c_random, alice_pk, alice_sk); sim->advance_time(1000); // Ensure mono_time > 0 mono_time = mono_time_new( &node->c_memory, [](void *_Nullable user_data) -> std::uint64_t { return static_cast(user_data)->clock().current_time_ms(); }, sim.get()); mono_time_update(mono_time); logger = logger_new(&node->c_memory); cache = shared_key_cache_new(logger, mono_time, &node->c_memory, alice_sk, 10000, 4); ASSERT_NE(cache, nullptr); } void TearDown() override { shared_key_cache_free(cache); logger_kill(logger); mono_time_free(&node->c_memory, mono_time); } std::unique_ptr sim; std::unique_ptr node; std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; std::uint8_t alice_sk[CRYPTO_SECRET_KEY_SIZE]; Mono_Time *_Nullable mono_time = nullptr; Logger *_Nullable logger = nullptr; Shared_Key_Cache *_Nullable cache = nullptr; }; TEST_F(SharedKeyCacheTest, BasicLookup) { std::uint8_t bob_pk[CRYPTO_PUBLIC_KEY_SIZE], bob_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&node->c_random, bob_pk, bob_sk); const std::uint8_t *shared1 = shared_key_cache_lookup(cache, bob_pk); ASSERT_NE(shared1, nullptr); // Second lookup should return cached pointer const std::uint8_t *shared2 = shared_key_cache_lookup(cache, bob_pk); EXPECT_EQ(shared1, shared2); // Verify key correctness std::uint8_t expected[CRYPTO_SHARED_KEY_SIZE]; encrypt_precompute(bob_pk, alice_sk, expected); EXPECT_EQ(std::memcmp(shared1, expected, CRYPTO_SHARED_KEY_SIZE), 0); } TEST_F(SharedKeyCacheTest, TimeoutEviction) { std::uint8_t bob_pk[CRYPTO_PUBLIC_KEY_SIZE], bob_sk[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(&node->c_random, bob_pk, bob_sk); shared_key_cache_lookup(cache, bob_pk); sim->advance_time(11000); // Past 10s timeout mono_time_update(mono_time); // Should re-compute/re-insert after timeout const std::uint8_t *shared = shared_key_cache_lookup(cache, bob_pk); ASSERT_NE(shared, nullptr); } TEST_F(SharedKeyCacheTest, SlotExhaustionAndLRU) { // Fill one bucket (4 slots) std::vector> pks; // Store pointers to verify cache hits (stable memory addresses) std::vector pointers; std::uint8_t target_hash = 0x42; for (int i = 0; i < 5; ++i) { std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE], sk[CRYPTO_SECRET_KEY_SIZE]; while (true) { crypto_new_keypair(&node->c_random, pk, sk); if (pk[8] == target_hash) break; } pks.push_back(std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE)); sim->advance_time(100); mono_time_update(mono_time); pointers.push_back(shared_key_cache_lookup(cache, pk)); } // 5th lookup should have evicted the oldest (LRU) bool reused = false; for (int i = 0; i < 4; ++i) { if (pointers[4] == pointers[i]) reused = true; } EXPECT_TRUE(reused); // Most recent should still be cached EXPECT_EQ(shared_key_cache_lookup(cache, pks[1].data()), pointers[1]); } TEST_F(SharedKeyCacheTest, HashDistribution) { const int total_keys = 256; std::vector> pks; // Store pointers to verify cache hits (stable memory addresses) std::vector pointers; // Fill cache with keys having unique hashes for (int i = 0; i < total_keys; ++i) { std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE], sk[CRYPTO_SECRET_KEY_SIZE]; while (true) { crypto_new_keypair(&node->c_random, pk, sk); std::uint8_t h = pk[8]; bool duplicate = false; for (const auto &existing : pks) { if (existing[8] == h) { duplicate = true; break; } } if (!duplicate) break; } pks.push_back(std::vector(pk, pk + CRYPTO_PUBLIC_KEY_SIZE)); pointers.push_back(shared_key_cache_lookup(cache, pk)); } // Verify high hit rate (low collisions) int hits = 0; for (int i = 0; i < total_keys; ++i) { if (shared_key_cache_lookup(cache, pks[i].data()) == pointers[i]) hits++; } EXPECT_EQ(hits, total_keys); } } // namespace