OpenTTD Source 20260206-master-g4d4e37dbf1
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "../stdafx.h"
11
12#include "../3rdparty/catch2/catch.hpp"
13
14#include "../core/format.hpp"
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
25class MockNetworkSocketHandler : public NetworkSocketHandler {
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
56class TestPasswordRequestHandler : public NetworkAuthenticationPasswordRequestHandler {
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.
std::string password
The entered password.
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:189
std::unique_ptr< class NetworkEncryptionHandler > send_encryption_handler
The handler for encrypting sent packets.
Definition core.h:48
std::unique_ptr< class NetworkEncryptionHandler > receive_encryption_handler
The handler for decrypting received packets.
Definition core.h:47
NetworkSocketHandler()=default
Create a new unbound socket.
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:94
static const size_t COMPAT_MTU
Number of bytes we can pack in a single packet for backward compatibility.
Definition config.h:44
static const uint NETWORK_PUBLIC_KEY_LENGTH
The maximum length of the hexadecimal encoded public keys, in bytes including '\0'.
Definition config.h:99
String formatting functions and helpers.
Internal bits to the crypto of the network handling.
constexpr size_t X25519_KEY_SIZE
The number of bytes the public and secret keys are in X25519.
Basic functions to create, fill and read packets.
uint8_t PacketType
Identifier for the packet.
Definition packet.h:20
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
Functions related to low-level strings.
Internal entity of a packet.
Definition packet.h:41
size_t Size() const
Get the number of bytes in the packet.
Definition packet.cpp:248
ssize_t TransferOutWithLimit(F transfer_function, size_t limit)
Transfer data from the packet to the given function.
Definition packet.h:103
void PrepareToSend()
Writes the packet size from the raw packet from packet->size.
Definition packet.cpp:64
void Send_uint64(uint64_t data)
Package a 64 bits integer in the packet.
Definition packet.cpp:152
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.