Files
libsession-util/tests/test_multi_encrypt.cpp
Morgan Pretty 0579f5c206 Merge branch 'dev' into multipart
# Conflicts:
#	external/CMakeLists.txt
#	external/oxen-encoding
#	include/session/config.hpp
#	include/session/config/base.hpp
#	include/session/config/convo_info_volatile.hpp
#	include/session/config/protos.hpp
#	include/session/hash.hpp
#	include/session/session_encrypt.hpp
#	include/session/types.hpp
#	include/session/util.hpp
#	src/CMakeLists.txt
#	src/config/base.cpp
#	src/config/convo_info_volatile.cpp
#	src/config/encrypt.cpp
#	src/config/internal.cpp
#	src/config/internal.hpp
#	src/config/protos.cpp
#	src/hash.cpp
#	src/session_encrypt.cpp
#	tests/CMakeLists.txt
#	tests/swarm-auth-test.cpp
#	tests/test_blinding.cpp
#	tests/test_bugs.cpp
#	tests/test_config_contacts.cpp
#	tests/test_config_convo_info_volatile.cpp
#	tests/test_config_user_groups.cpp
#	tests/test_config_userprofile.cpp
#	tests/test_configdata.cpp
#	tests/test_group_info.cpp
#	tests/test_group_keys.cpp
#	tests/test_group_members.cpp
#	tests/test_multi_encrypt.cpp
#	tests/test_session_encrypt.cpp
#	tests/utils.hpp
2025-04-11 11:53:50 +10:00

354 lines
14 KiB
C++

#include <fmt/core.h>
#include <sodium/crypto_aead_xchacha20poly1305.h>
#include <sodium/crypto_sign_ed25519.h>
#include <catch2/catch_test_macros.hpp>
#include <session/multi_encrypt.hpp>
#include <session/util.hpp>
#include "utils.hpp"
using x_pair = std::pair<std::array<unsigned char, 32>, std::array<unsigned char, 32>>;
// Returns X25519 privkey, pubkey from an Ed25519 seed
x_pair to_x_keys(std::span<const unsigned char> ed_seed) {
std::array<unsigned char, 32> ed_pk;
std::array<unsigned char, 64> ed_sk;
crypto_sign_ed25519_seed_keypair(ed_pk.data(), ed_sk.data(), ed_seed.data());
x_pair ret;
auto& [x_priv, x_pub] = ret;
[[maybe_unused]] int rc = crypto_sign_ed25519_pk_to_curve25519(x_pub.data(), ed_pk.data());
assert(rc == 0);
crypto_sign_ed25519_sk_to_curve25519(x_priv.data(), ed_sk.data());
return ret;
}
TEST_CASE("Multi-recipient encryption", "[encrypt][multi]") {
const std::array seeds = {
"0123456789abcdef0123456789abcdef00000000000000000000000000000000"_hexbytes,
"0123456789abcdef000000000000000000000000000000000000000000000000"_hexbytes,
"0123456789abcdef111111111111111100000000000000000000000000000000"_hexbytes,
"0123456789abcdef222222222222222200000000000000000000000000000000"_hexbytes,
"0123456789abcdef333333333333333300000000000000000000000000000000"_hexbytes};
std::array<x_pair, seeds.size()> x_keys;
for (size_t i = 0; i < seeds.size(); i++)
x_keys[i] = to_x_keys(seeds[i]);
CHECK(oxenc::to_hex(session::to_span(x_keys[0].second)) ==
"d2ad010eeb72d72e561d9de7bd7b6989af77dcabffa03a5111a6c859ae5c3a72");
CHECK(oxenc::to_hex(session::to_span(x_keys[1].second)) ==
"d673a8fb4800d2a252d2fc4e3342a88cdfa9412853934e8993d12d593be13371");
CHECK(oxenc::to_hex(session::to_span(x_keys[2].second)) ==
"afd9716ea69ab8c7f475e1b250c86a6539e260804faecf2a803e9281a4160738");
CHECK(oxenc::to_hex(session::to_span(x_keys[3].second)) ==
"03be14feabd59122349614b88bdc90db1d1af4c230e9a73c898beec833d51f11");
CHECK(oxenc::to_hex(session::to_span(x_keys[4].second)) ==
"27b5c1ea87cef76284c752fa6ee1b9186b1a95e74e8f5b88f8b47e5191ce6f08");
auto nonce = "32ab4bb45d6df5cc14e1c330fb1a8b68ea3826a8c2213a49"_hexbytes;
std::vector<std::span<const unsigned char>> recipients;
for (auto& [_, pubkey] : x_keys)
recipients.emplace_back(pubkey.data(), pubkey.size());
std::vector<std::string> msgs{{"hello", "cruel", "world"}};
std::vector<std::vector<unsigned char>> encrypted;
session::encrypt_for_multiple(
msgs[0],
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
nonce,
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite",
[&](std::span<const unsigned char> enc) {
encrypted.emplace_back(session::to_vector(enc));
});
REQUIRE(encrypted.size() == 3);
CHECK(to_hex(encrypted[0]) == "e64937e5ea201b84f4e88a976dad900d91caaf6a17");
CHECK(to_hex(encrypted[1]) == "b7a15bcd9f7b09445defcae2f1dc5085dd75cb085b");
CHECK(to_hex(encrypted[2]) == "01c4fc2156327735f3fb5063b11ea95f6ebcc5b6cc");
auto m1 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[1].first),
session::to_span(x_keys[1].second),
session::to_span(x_keys[0].second),
"test suite");
auto m2 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[2].first),
session::to_span(x_keys[2].second),
session::to_span(x_keys[0].second),
"test suite");
auto m3 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"test suite");
auto m3b = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"not test suite");
auto m4 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[4].first),
session::to_span(x_keys[4].second),
session::to_span(x_keys[0].second),
"test suite");
REQUIRE(m1);
REQUIRE(m2);
REQUIRE(m3);
CHECK_FALSE(m3b);
CHECK_FALSE(m4);
CHECK(session::to_string(*m1) == "hello");
CHECK(session::to_string(*m2) == "hello");
CHECK(session::to_string(*m3) == "hello");
encrypted.clear();
session::encrypt_for_multiple(
session::to_view_vector(msgs.begin(), msgs.end()),
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
nonce,
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite",
[&](std::span<const unsigned char> enc) {
encrypted.emplace_back(session::to_vector(enc));
});
REQUIRE(encrypted.size() == 3);
CHECK(to_hex(encrypted[0]) == "e64937e5ea201b84f4e88a976dad900d91caaf6a17");
CHECK(to_hex(encrypted[1]) == "bcb642c49c6da03f70cdaab2ed6666721318afd631");
CHECK(to_hex(encrypted[2]) == "1ecee2215d226817edfdb097f05037eb799309103a");
m1 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[1].first),
session::to_span(x_keys[1].second),
session::to_span(x_keys[0].second),
"test suite");
m2 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[2].first),
session::to_span(x_keys[2].second),
session::to_span(x_keys[0].second),
"test suite");
m3 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"test suite");
m3b = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"not test suite");
m4 = session::decrypt_for_multiple(
session::to_view_vector(encrypted),
nonce,
session::to_span(x_keys[4].first),
session::to_span(x_keys[4].second),
session::to_span(x_keys[0].second),
"test suite");
REQUIRE(m1);
REQUIRE(m2);
REQUIRE(m3);
CHECK_FALSE(m3b);
CHECK_FALSE(m4);
CHECK(session::to_string(*m1) == "hello");
CHECK(session::to_string(*m2) == "cruel");
CHECK(session::to_string(*m3) == "world");
// Mismatch messages & recipients size throws:
CHECK_THROWS(session::encrypt_for_multiple(
session::to_view_vector(msgs.begin(), std::prev(msgs.end())),
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
nonce,
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite",
[&](std::span<const unsigned char> enc) {
encrypted.emplace_back(session::to_vector(enc));
}));
}
TEST_CASE("Multi-recipient encryption, simpler interface", "[encrypt][multi][simple]") {
const std::array seeds = {
"0123456789abcdef0123456789abcdef00000000000000000000000000000000"_hexbytes,
"0123456789abcdef000000000000000000000000000000000000000000000000"_hexbytes,
"0123456789abcdef111111111111111100000000000000000000000000000000"_hexbytes,
"0123456789abcdef222222222222222200000000000000000000000000000000"_hexbytes,
"0123456789abcdef333333333333333300000000000000000000000000000000"_hexbytes};
std::array<x_pair, seeds.size()> x_keys;
for (size_t i = 0; i < seeds.size(); i++)
x_keys[i] = to_x_keys(seeds[i]);
CHECK(oxenc::to_hex(session::to_span(x_keys[0].second)) ==
"d2ad010eeb72d72e561d9de7bd7b6989af77dcabffa03a5111a6c859ae5c3a72");
CHECK(oxenc::to_hex(session::to_span(x_keys[1].second)) ==
"d673a8fb4800d2a252d2fc4e3342a88cdfa9412853934e8993d12d593be13371");
CHECK(oxenc::to_hex(session::to_span(x_keys[2].second)) ==
"afd9716ea69ab8c7f475e1b250c86a6539e260804faecf2a803e9281a4160738");
CHECK(oxenc::to_hex(session::to_span(x_keys[3].second)) ==
"03be14feabd59122349614b88bdc90db1d1af4c230e9a73c898beec833d51f11");
CHECK(oxenc::to_hex(session::to_span(x_keys[4].second)) ==
"27b5c1ea87cef76284c752fa6ee1b9186b1a95e74e8f5b88f8b47e5191ce6f08");
auto nonce = "32ab4bb45d6df5cc14e1c330fb1a8b68ea3826a8c2213a49"_hexbytes;
std::vector<std::span<const unsigned char>> recipients;
for (auto& [_, pubkey] : x_keys)
recipients.emplace_back(pubkey.data(), pubkey.size());
std::vector<std::string> msgs{{"hello", "cruel", "world"}};
std::vector<unsigned char> encrypted = session::encrypt_for_multiple_simple(
msgs[0],
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite");
REQUIRE(encrypted.size() ==
/* de */ 2 +
/* 1:# 24:...nonce... */ 3 + 27 +
/* 1:e le */ 3 + 2 +
/* XX: then data with overhead */ 3 *
(3 + 5 + crypto_aead_xchacha20poly1305_ietf_ABYTES));
// If we encrypt again the value should be different (because of the default randomized nonce):
CHECK(encrypted != session::encrypt_for_multiple_simple(
msgs[0],
session::to_view_vector(
std::next(recipients.begin()), std::prev(recipients.end())),
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite"));
auto m1 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[1].first),
session::to_span(x_keys[1].second),
session::to_span(x_keys[0].second),
"test suite");
auto m2 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[2].first),
session::to_span(x_keys[2].second),
session::to_span(x_keys[0].second),
"test suite");
auto m3 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"test suite");
auto m3b = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"not test suite");
auto m4 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[4].first),
session::to_span(x_keys[4].second),
session::to_span(x_keys[0].second),
"test suite");
REQUIRE(m1);
REQUIRE(m2);
REQUIRE(m3);
CHECK_FALSE(m3b);
CHECK_FALSE(m4);
CHECK(session::to_string(*m1) == "hello");
CHECK(session::to_string(*m2) == "hello");
CHECK(session::to_string(*m3) == "hello");
encrypted = session::encrypt_for_multiple_simple(
session::to_view_vector(msgs),
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite",
nonce);
CHECK(printable(encrypted) == printable(
"d1:#24:{}1:el21:{}21:{}21:{}ee",
"32ab4bb45d6df5cc14e1c330fb1a8b68ea3826a8c2213a49"_hex,
"e64937e5ea201b84f4e88a976dad900d91caaf6a17"_hex,
"bcb642c49c6da03f70cdaab2ed6666721318afd631"_hex,
"1ecee2215d226817edfdb097f05037eb799309103a"_hex));
m1 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[1].first),
session::to_span(x_keys[1].second),
session::to_span(x_keys[0].second),
"test suite");
m2 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[2].first),
session::to_span(x_keys[2].second),
session::to_span(x_keys[0].second),
"test suite");
m3 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"test suite");
m3b = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[3].first),
session::to_span(x_keys[3].second),
session::to_span(x_keys[0].second),
"not test suite");
m4 = session::decrypt_for_multiple_simple(
encrypted,
session::to_span(x_keys[4].first),
session::to_span(x_keys[4].second),
session::to_span(x_keys[0].second),
"test suite");
REQUIRE(m1);
REQUIRE(m2);
REQUIRE(m3);
CHECK_FALSE(m3b);
CHECK_FALSE(m4);
CHECK(session::to_string(*m1) == "hello");
CHECK(session::to_string(*m2) == "cruel");
CHECK(session::to_string(*m3) == "world");
CHECK_THROWS(session::encrypt_for_multiple_simple(
session::to_view_vector(msgs.begin(), std::prev(msgs.end())),
session::to_view_vector(std::next(recipients.begin()), std::prev(recipients.end())),
session::to_span(x_keys[0].first),
session::to_span(x_keys[0].second),
"test suite"));
}