From 249c727bbba0277ac429552e9f0ca007f3fb0896 Mon Sep 17 00:00:00 2001 From: Samy Date: Thu, 3 Sep 2026 00:24:44 +0200 Subject: [PATCH] onionreq: reject truncated XChaCha20 ciphertexts --- src/onionreq/hop_encryption.cpp | 10 +++++----- tests/test_onionreq.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/onionreq/hop_encryption.cpp b/src/onionreq/hop_encryption.cpp index 69a491c6c..7d1248842 100644 --- a/src/onionreq/hop_encryption.cpp +++ b/src/onionreq/hop_encryption.cpp @@ -211,17 +211,17 @@ std::vector HopEncryption::encrypt_xchacha20( std::vector HopEncryption::decrypt_xchacha20( std::vector ciphertext_, const network::x25519_pubkey& pubKey) const { + if (ciphertext_.size() < + crypto_aead_xchacha20poly1305_ietf_NPUBBYTES + crypto_aead_xchacha20poly1305_ietf_ABYTES) + throw std::invalid_argument{ + "Invalid ciphertext: too short to contain valid encrypted data"}; + std::span ciphertext = to_span(ciphertext_); // Extract nonce from the beginning of the ciphertext: auto nonce = ciphertext.subspan(0, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); ciphertext = ciphertext.subspan(nonce.size()); - if (!response_long_enough(EncryptType::xchacha20, ciphertext_.size())) - throw std::invalid_argument{ - "Ciphertext data is too short: " + - std::string(reinterpret_cast(ciphertext_.data()))}; - const auto key = xchacha20_shared_key(public_key_, private_key_, pubKey, !server_); std::vector plaintext; diff --git a/tests/test_onionreq.cpp b/tests/test_onionreq.cpp index 79f3bfd78..ef617780a 100644 --- a/tests/test_onionreq.cpp +++ b/tests/test_onionreq.cpp @@ -42,6 +42,18 @@ TEST_CASE("Onion request encryption", "[encryption][onionreq]") { CHECK_THROWS(e.decrypt_aesgcm(enc_xchacha20_broken2, x25519_pubkey::from_bytes(A))); CHECK_THROWS(e.decrypt_xchacha20(enc_xchacha20_broken1, x25519_pubkey::from_bytes(A))); CHECK_THROWS(e.decrypt_xchacha20(enc_xchacha20_broken2, x25519_pubkey::from_bytes(A))); + + auto enc_xchacha20_empty = e.encrypt_xchacha20({}, x25519_pubkey::from_bytes(A)); + CHECK(enc_xchacha20_empty.size() == 24 + 16); + CHECK(e.decrypt_xchacha20(enc_xchacha20_empty, x25519_pubkey::from_bytes(A)).empty()); + + for (size_t size : {0, 1, 15, 16, 23, 24, 39}) { + INFO("ciphertext size: " << size); + CHECK_THROWS_AS( + e.decrypt_xchacha20( + std::vector(size, 0x41), x25519_pubkey::from_bytes(A)), + std::invalid_argument); + } } TEST_CASE("Onion request parser", "[onionreq][parser]") {