OpenTTD Source 20250521-master-g82876c25e0
test_network_crypto.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "../stdafx.h"
11
12#include "../3rdparty/catch2/catch.hpp"
13
14#include "../core/format.hpp"
15#include "../network/network_crypto_internal.h"
16#include "../network/core/packet.h"
17#include "../string_func.h"
18
19#include "../safeguards.h"
20
21/* The length of the hexadecimal representation of a X25519 key must fit in the key length. */
22static_assert(NETWORK_SECRET_KEY_LENGTH >= X25519_KEY_SIZE * 2 + 1);
23static_assert(NETWORK_PUBLIC_KEY_LENGTH >= X25519_KEY_SIZE * 2 + 1);
24
26public:
27 MockNetworkSocketHandler(std::unique_ptr<NetworkEncryptionHandler> &&receive = {}, std::unique_ptr<NetworkEncryptionHandler> &&send = {})
28 {
29 this->receive_encryption_handler = std::move(receive);
30 this->send_encryption_handler = std::move(send);
31 }
32};
33
34static MockNetworkSocketHandler mock_socket_handler;
35
36static std::tuple<Packet, bool> CreatePacketForReading(Packet &source, MockNetworkSocketHandler *socket_handler)
37{
38 source.PrepareToSend();
39
40 Packet dest(socket_handler, COMPAT_MTU, source.Size());
41
42 auto transfer_in = [&source](std::span<uint8_t> dest_data) {
43 auto transfer_out = [&dest_data](std::span<const uint8_t> source_data) {
44 std::ranges::copy(source_data, dest_data.begin());
45 return source_data.size();
46 };
47 return source.TransferOutWithLimit(transfer_out, dest_data.size());
48 };
49 dest.TransferIn(transfer_in);
50
51 bool valid = dest.PrepareToRead();
52 dest.Recv_uint8(); // Ignore the type
53 return { dest, valid };
54}
55
57private:
58 std::string password;
59public:
60 TestPasswordRequestHandler(std::string &password) : password(password) {}
61 void SendResponse() override {}
62 void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) override { request->Reply(this->password); }
63};
64
65static void TestAuthentication(NetworkAuthenticationServerHandler &server, NetworkAuthenticationClientHandler &client,
68{
69 Packet request(&mock_socket_handler, PacketType{});
70 server.SendRequest(request);
71
72 bool valid;
73 std::tie(request, valid) = CreatePacketForReading(request, &mock_socket_handler);
74 CHECK(valid);
75 CHECK(client.ReceiveRequest(request) == expected_request_result);
76
77 Packet response(&mock_socket_handler, PacketType{});
78 client.SendResponse(response);
79
80 std::tie(response, valid) = CreatePacketForReading(response, &mock_socket_handler);
81 CHECK(valid);
82 CHECK(server.ReceiveResponse(response) == expected_response_result);
83}
84
85
86TEST_CASE("Authentication_KeyExchangeOnly")
87{
90
92}
93
94
95static void TestAuthenticationPAKE(std::string server_password, std::string client_password,
97{
98 NetworkAuthenticationDefaultPasswordProvider server_password_provider(server_password);
99 X25519PAKEServerHandler server(X25519SecretKey::CreateRandom(), &server_password_provider);
100 X25519PAKEClientHandler client(X25519SecretKey::CreateRandom(), std::make_shared<TestPasswordRequestHandler>(client_password));
101
102 TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::RequestResult::AwaitUserInput);
103}
104
105TEST_CASE("Authentication_PAKE")
106{
107 SECTION("Correct password") {
108 TestAuthenticationPAKE("sikrit", "sikrit", NetworkAuthenticationServerHandler::ResponseResult::Authenticated);
109 }
110
111 SECTION("Empty password") {
113 }
114
115 SECTION("Wrong password") {
116 TestAuthenticationPAKE("sikrit", "secret", NetworkAuthenticationServerHandler::ResponseResult::NotAuthenticated);
117 }
118}
119
120
121static void TestAuthenticationAuthorizedKey(const X25519SecretKey &client_secret_key, const X25519PublicKey &server_expected_public_key,
123{
124 NetworkAuthorizedKeys authorized_keys;
125 authorized_keys.Add(FormatArrayAsHex(server_expected_public_key));
126
127 NetworkAuthenticationDefaultAuthorizedKeyHandler authorized_key_handler(authorized_keys);
128 X25519AuthorizedKeyServerHandler server(X25519SecretKey::CreateRandom(), &authorized_key_handler);
129 X25519AuthorizedKeyClientHandler client(client_secret_key);
130
131 TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::RequestResult::ReadyForResponse);
132}
133
134TEST_CASE("Authentication_AuthorizedKey")
135{
136 auto client_secret_key = X25519SecretKey::CreateRandom();
137 auto valid_client_public_key = client_secret_key.CreatePublicKey();
138 auto invalid_client_public_key = X25519SecretKey::CreateRandom().CreatePublicKey();
139
140 SECTION("Correct public key") {
141 TestAuthenticationAuthorizedKey(client_secret_key, valid_client_public_key, NetworkAuthenticationServerHandler::ResponseResult::Authenticated);
142 }
143
144 SECTION("Incorrect public key") {
145 TestAuthenticationAuthorizedKey(client_secret_key, invalid_client_public_key, NetworkAuthenticationServerHandler::ResponseResult::NotAuthenticated);
146 }
147}
148
149
150TEST_CASE("Authentication_Combined")
151{
152 auto client_secret_key = X25519SecretKey::CreateRandom();
153 std::string client_secret_key_str = FormatArrayAsHex(client_secret_key);
154 auto client_public_key = client_secret_key.CreatePublicKey();
155 std::string client_public_key_str = FormatArrayAsHex(client_public_key);
156
157 NetworkAuthorizedKeys valid_authorized_keys;
158 valid_authorized_keys.Add(client_public_key_str);
159 NetworkAuthenticationDefaultAuthorizedKeyHandler valid_authorized_key_handler(valid_authorized_keys);
160
161 NetworkAuthorizedKeys invalid_authorized_keys;
162 invalid_authorized_keys.Add("not-a-valid-authorized-key");
163 NetworkAuthenticationDefaultAuthorizedKeyHandler invalid_authorized_key_handler(invalid_authorized_keys);
164
165 NetworkAuthorizedKeys no_authorized_keys;
166 NetworkAuthenticationDefaultAuthorizedKeyHandler no_authorized_key_handler(no_authorized_keys);
167
168 std::string no_password = "";
169 NetworkAuthenticationDefaultPasswordProvider no_password_provider(no_password);
170 std::string valid_password = "sikrit";
171 NetworkAuthenticationDefaultPasswordProvider valid_password_provider(valid_password);
172 std::string invalid_password = "secret";
173 NetworkAuthenticationDefaultPasswordProvider invalid_password_provider(invalid_password);
174
175 auto client = NetworkAuthenticationClientHandler::Create(std::make_shared<TestPasswordRequestHandler>(valid_password), client_secret_key_str, client_public_key_str);
176
177 SECTION("Invalid authorized keys, invalid password") {
178 auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &invalid_authorized_key_handler);
179
182 }
183
184 SECTION("Invalid authorized keys, valid password") {
185 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &invalid_authorized_key_handler);
186
189 }
190
191 SECTION("Valid authorized keys, valid password") {
192 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &valid_authorized_key_handler);
193
195 }
196
197 SECTION("No authorized keys, invalid password") {
198 auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &no_authorized_key_handler);
199
201 }
202
203 SECTION("No authorized keys, valid password") {
204 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &no_authorized_key_handler);
205
207 }
208
209 SECTION("No authorized keys, no password") {
210 auto server = NetworkAuthenticationServerHandler::Create(&no_password_provider, &no_authorized_key_handler);
211
213 }
214}
215
216
217static void CheckEncryption(MockNetworkSocketHandler *sending_socket_handler, MockNetworkSocketHandler *receiving_socket_handler)
218{
219 PacketType sent_packet_type{ 1 };
220 uint64_t sent_value = 0x1234567890ABCDEF;
221 std::set<PacketType> encrypted_packet_types;
222
223 for (int i = 0; i < 10; i++) {
224 Packet request(sending_socket_handler, sent_packet_type);
225 request.Send_uint64(sent_value);
226
227 auto [response, valid] = CreatePacketForReading(request, receiving_socket_handler);
228 CHECK(valid);
229 CHECK(response.Recv_uint64() == sent_value);
230
231 encrypted_packet_types.insert(request.GetPacketType());
232 }
233 /*
234 * Check whether it looks like encryption has happened. This is done by checking the value
235 * of the packet type after encryption. If after a few iterations more than one encrypted
236 * value has been seen, then we know that some type of encryption/scrambling is happening.
237 *
238 * Technically this check could fail erroneously when 16 subsequent encryptions yield the
239 * same encrypted packet type. However, with encryption that byte should have random value
240 * value, so the chance of this happening are tiny given enough iterations.
241 * Roughly in the order of 2**((iterations - 1) * 8), which with 10 iterations is in the
242 * one-in-sextillion (10**21) order of magnitude.
243 */
244 CHECK(encrypted_packet_types.size() != 1);
245
246}
247
248TEST_CASE("Encryption handling")
249{
252
254
255 Packet packet(&mock_socket_handler, PacketType{});
256 server.SendEnableEncryption(packet);
257
258 bool valid;
259 std::tie(packet, valid) = CreatePacketForReading(packet, &mock_socket_handler);
260 CHECK(valid);
261 CHECK(client.ReceiveEnableEncryption(packet));
262
265
266 SECTION("Encryption happening client -> server") {
267 CheckEncryption(&client_socket_handler, &server_socket_handler);
268 }
269
270 SECTION("Encryption happening server -> client") {
271 CheckEncryption(&server_socket_handler, &client_socket_handler);
272 }
273
274 SECTION("Unencrypted packet sent causes invalid read packet") {
275 Packet request(&mock_socket_handler, PacketType{});
276 request.Send_uint64(0);
277
278 auto [response, valid] = CreatePacketForReading(request, &client_socket_handler);
279 CHECK(!valid);
280 }
281}
Base class for client side cryptographic authentication handlers.
RequestResult
The processing result of receiving a request.
@ AwaitUserInput
We have requested some user input, but must wait on that.
@ ReadyForResponse
We do not have to wait for user input, and can immediately respond to the server.
virtual bool ReceiveEnableEncryption(struct Packet &p)=0
Read the request to enable encryption from the server.
virtual RequestResult ReceiveRequest(struct Packet &p)=0
Read a request from the server.
virtual bool SendResponse(struct Packet &p)=0
Create the response to send to the server.
static std::unique_ptr< NetworkAuthenticationClientHandler > Create(std::shared_ptr< NetworkAuthenticationPasswordRequestHandler > password_handler, std::string &secret_key, std::string &public_key)
Create a NetworkAuthenticationClientHandler.
Default implementation for the authorized key handler.
Default implementation of the password provider.
virtual std::unique_ptr< NetworkEncryptionHandler > CreateServerToClientEncryptionHandler() const =0
Create a NetworkEncryptionHandler to encrypt or decrypt messages from the server to the client.
virtual std::unique_ptr< NetworkEncryptionHandler > CreateClientToServerEncryptionHandler() const =0
Create a NetworkEncryptionHandler to encrypt or decrypt messages from the client to the server.
Callback interface for client implementations to provide the handling of the password requests.
Base class for server side cryptographic authentication handlers.
virtual ResponseResult ReceiveResponse(struct Packet &p)=0
Read the response from the client.
virtual void SendEnableEncryption(struct Packet &p)=0
Create the request to enable encryption to the client.
virtual void SendRequest(struct Packet &p)=0
Create the request to send to the client.
static std::unique_ptr< NetworkAuthenticationServerHandler > Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask={NetworkAuthenticationMethod::X25519_KeyExchangeOnly, NetworkAuthenticationMethod::X25519_PAKE, NetworkAuthenticationMethod::X25519_AuthorizedKey})
Create a NetworkAuthenticationServerHandler.
ResponseResult
The processing result of receiving a response.
@ RetryNextMethod
The client failed to authenticate, but there is another method to try.
@ NotAuthenticated
All authentications for this handler have been exhausted.
@ Authenticated
The client was authenticated successfully.
Simple helper to (more easily) manage authorized keys.
bool Add(std::string_view key)
Add the given key to the authorized keys, when it is not already contained.
Definition network.cpp:190
SocketHandler for all network sockets in OpenTTD.
Definition core.h:43
std::unique_ptr< class NetworkEncryptionHandler > send_encryption_handler
The handler for encrypting sent packets.
Definition core.h:50
std::unique_ptr< class NetworkEncryptionHandler > receive_encryption_handler
The handler for decrypting received packets.
Definition core.h:49
void SendResponse() override
Callback to trigger sending the response for the password request.
void AskUserForPassword(std::shared_ptr< NetworkAuthenticationPasswordRequest > request) override
Callback to trigger asking the user for the password.
Handler for clients using a X25519 key exchange to perform authentication via a set of authorized (pu...
Handler for servers using a X25519 key exchange to perform authentication via a set of authorized (pu...
Client side handler for using X25519 without actual authentication.
Server side handler for using X25519 without actual authentication.
Client side handler for using X25519 with a password-authenticated key exchange.
Server side handler for using X25519 with a password-authenticated key exchange.
static const uint NETWORK_SECRET_KEY_LENGTH
The maximum length of the hexadecimal encoded secret keys, in bytes including '\0'.
Definition config.h:96
static const size_t COMPAT_MTU
Number of bytes we can pack in a single packet for backward compatibility.
Definition config.h:46
static const uint NETWORK_PUBLIC_KEY_LENGTH
The maximum length of the hexadecimal encoded public keys, in bytes including '\0'.
Definition config.h:101
constexpr size_t X25519_KEY_SIZE
The number of bytes the public and secret keys are in X25519.
uint8_t PacketType
Identifier for the packet.
Definition packet.h:22
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:75
Internal entity of a packet.
Definition packet.h:43
size_t Size() const
Get the number of bytes in the packet.
Definition packet.cpp:250
ssize_t TransferOutWithLimit(F transfer_function, size_t limit)
Transfer data from the packet to the given function.
Definition packet.h:105
void PrepareToSend()
Writes the packet size from the raw packet from packet->size.
Definition packet.cpp:66
void Send_uint64(uint64_t data)
Package a 64 bits integer in the packet.
Definition packet.cpp:154
Container for a X25519 public key.
Container for a X25519 secret key.
X25519PublicKey CreatePublicKey() const
Create the public key associated with this secret key.
static X25519SecretKey CreateRandom()
Create a new secret key that's filled with random bytes.