OpenTTD Source 20241224-master-gee860a5c8e
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/* The length of the hexadecimal representation of a X25519 key must fit in the key length. */
20static_assert(NETWORK_SECRET_KEY_LENGTH >= X25519_KEY_SIZE * 2 + 1);
21static_assert(NETWORK_PUBLIC_KEY_LENGTH >= X25519_KEY_SIZE * 2 + 1);
22
24public:
25 MockNetworkSocketHandler(std::unique_ptr<NetworkEncryptionHandler> &&receive = {}, std::unique_ptr<NetworkEncryptionHandler> &&send = {})
26 {
27 this->receive_encryption_handler = std::move(receive);
28 this->send_encryption_handler = std::move(send);
29 }
30};
31
32static MockNetworkSocketHandler mock_socket_handler;
33
34static std::tuple<Packet, bool> CreatePacketForReading(Packet &source, MockNetworkSocketHandler *socket_handler)
35{
36 source.PrepareToSend();
37
38 Packet dest(socket_handler, COMPAT_MTU, source.Size());
39
40 auto transfer_in = [](Packet &source, char *dest_data, size_t length) {
41 auto transfer_out = [](char *dest_data, const char *source_data, size_t length) {
42 std::copy(source_data, source_data + length, dest_data);
43 return length;
44 };
45 return source.TransferOutWithLimit(transfer_out, length, dest_data);
46 };
47 dest.TransferIn(transfer_in, source);
48
49 bool valid = dest.PrepareToRead();
50 dest.Recv_uint8(); // Ignore the type
51 return { dest, valid };
52}
53
55private:
56 std::string password;
57public:
58 TestPasswordRequestHandler(std::string &password) : password(password) {}
59 void SendResponse() override {}
60 void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) override { request->Reply(this->password); }
61};
62
63static void TestAuthentication(NetworkAuthenticationServerHandler &server, NetworkAuthenticationClientHandler &client,
66{
67 Packet request(&mock_socket_handler, PacketType{});
68 server.SendRequest(request);
69
70 bool valid;
71 std::tie(request, valid) = CreatePacketForReading(request, &mock_socket_handler);
72 CHECK(valid);
73 CHECK(client.ReceiveRequest(request) == expected_request_result);
74
75 Packet response(&mock_socket_handler, PacketType{});
76 client.SendResponse(response);
77
78 std::tie(response, valid) = CreatePacketForReading(response, &mock_socket_handler);
79 CHECK(valid);
80 CHECK(server.ReceiveResponse(response) == expected_response_result);
81}
82
83
84TEST_CASE("Authentication_KeyExchangeOnly")
85{
88
90}
91
92
93static void TestAuthenticationPAKE(std::string server_password, std::string client_password,
95{
96 NetworkAuthenticationDefaultPasswordProvider server_password_provider(server_password);
97 X25519PAKEServerHandler server(X25519SecretKey::CreateRandom(), &server_password_provider);
98 X25519PAKEClientHandler client(X25519SecretKey::CreateRandom(), std::make_shared<TestPasswordRequestHandler>(client_password));
99
100 TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
101}
102
103TEST_CASE("Authentication_PAKE")
104{
105 SECTION("Correct password") {
106 TestAuthenticationPAKE("sikrit", "sikrit", NetworkAuthenticationServerHandler::AUTHENTICATED);
107 }
108
109 SECTION("Empty password") {
110 TestAuthenticationPAKE("", "", NetworkAuthenticationServerHandler::AUTHENTICATED);
111 }
112
113 SECTION("Wrong password") {
114 TestAuthenticationPAKE("sikrit", "secret", NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
115 }
116}
117
118
119static void TestAuthenticationAuthorizedKey(const X25519SecretKey &client_secret_key, const X25519PublicKey &server_expected_public_key,
121{
122 NetworkAuthorizedKeys authorized_keys;
123 authorized_keys.Add(FormatArrayAsHex(server_expected_public_key));
124
125 NetworkAuthenticationDefaultAuthorizedKeyHandler authorized_key_handler(authorized_keys);
126 X25519AuthorizedKeyServerHandler server(X25519SecretKey::CreateRandom(), &authorized_key_handler);
127 X25519AuthorizedKeyClientHandler client(client_secret_key);
128
129 TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
130}
131
132TEST_CASE("Authentication_AuthorizedKey")
133{
134 auto client_secret_key = X25519SecretKey::CreateRandom();
135 auto valid_client_public_key = client_secret_key.CreatePublicKey();
136 auto invalid_client_public_key = X25519SecretKey::CreateRandom().CreatePublicKey();
137
138 SECTION("Correct public key") {
139 TestAuthenticationAuthorizedKey(client_secret_key, valid_client_public_key, NetworkAuthenticationServerHandler::AUTHENTICATED);
140 }
141
142 SECTION("Incorrect public key") {
143 TestAuthenticationAuthorizedKey(client_secret_key, invalid_client_public_key, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
144 }
145}
146
147
148TEST_CASE("Authentication_Combined")
149{
150 auto client_secret_key = X25519SecretKey::CreateRandom();
151 std::string client_secret_key_str = FormatArrayAsHex(client_secret_key);
152 auto client_public_key = client_secret_key.CreatePublicKey();
153 std::string client_public_key_str = FormatArrayAsHex(client_public_key);
154
155 NetworkAuthorizedKeys valid_authorized_keys;
156 valid_authorized_keys.Add(client_public_key_str);
157 NetworkAuthenticationDefaultAuthorizedKeyHandler valid_authorized_key_handler(valid_authorized_keys);
158
159 NetworkAuthorizedKeys invalid_authorized_keys;
160 invalid_authorized_keys.Add("not-a-valid-authorized-key");
161 NetworkAuthenticationDefaultAuthorizedKeyHandler invalid_authorized_key_handler(invalid_authorized_keys);
162
163 NetworkAuthorizedKeys no_authorized_keys;
164 NetworkAuthenticationDefaultAuthorizedKeyHandler no_authorized_key_handler(no_authorized_keys);
165
166 std::string no_password = "";
167 NetworkAuthenticationDefaultPasswordProvider no_password_provider(no_password);
168 std::string valid_password = "sikrit";
169 NetworkAuthenticationDefaultPasswordProvider valid_password_provider(valid_password);
170 std::string invalid_password = "secret";
171 NetworkAuthenticationDefaultPasswordProvider invalid_password_provider(invalid_password);
172
173 auto client = NetworkAuthenticationClientHandler::Create(std::make_shared<TestPasswordRequestHandler>(valid_password), client_secret_key_str, client_public_key_str);
174
175 SECTION("Invalid authorized keys, invalid password") {
176 auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &invalid_authorized_key_handler);
177
180 }
181
182 SECTION("Invalid authorized keys, valid password") {
183 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &invalid_authorized_key_handler);
184
187 }
188
189 SECTION("Valid authorized keys, valid password") {
190 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &valid_authorized_key_handler);
191
193 }
194
195 SECTION("No authorized keys, invalid password") {
196 auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &no_authorized_key_handler);
197
199 }
200
201 SECTION("No authorized keys, valid password") {
202 auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &no_authorized_key_handler);
203
205 }
206
207 SECTION("No authorized keys, no password") {
208 auto server = NetworkAuthenticationServerHandler::Create(&no_password_provider, &no_authorized_key_handler);
209
211 }
212}
213
214
215static void CheckEncryption(MockNetworkSocketHandler *sending_socket_handler, MockNetworkSocketHandler *receiving_socket_handler)
216{
217 PacketType sent_packet_type{ 1 };
218 uint64_t sent_value = 0x1234567890ABCDEF;
219 std::set<PacketType> encrypted_packet_types;
220
221 for (int i = 0; i < 10; i++) {
222 Packet request(sending_socket_handler, sent_packet_type);
223 request.Send_uint64(sent_value);
224
225 auto [response, valid] = CreatePacketForReading(request, receiving_socket_handler);
226 CHECK(valid);
227 CHECK(response.Recv_uint64() == sent_value);
228
229 encrypted_packet_types.insert(request.GetPacketType());
230 }
231 /*
232 * Check whether it looks like encryption has happened. This is done by checking the value
233 * of the packet type after encryption. If after a few iterations more than one encrypted
234 * value has been seen, then we know that some type of encryption/scrambling is happening.
235 *
236 * Technically this check could fail erroneously when 16 subsequent encryptions yield the
237 * same encrypted packet type. However, with encryption that byte should have random value
238 * value, so the chance of this happening are tiny given enough iterations.
239 * Roughly in the order of 2**((iterations - 1) * 8), which with 10 iterations is in the
240 * one-in-sextillion (10**21) order of magnitude.
241 */
242 CHECK(encrypted_packet_types.size() != 1);
243
244}
245
246TEST_CASE("Encryption handling")
247{
250
252
253 Packet packet(&mock_socket_handler, PacketType{});
254 server.SendEnableEncryption(packet);
255
256 bool valid;
257 std::tie(packet, valid) = CreatePacketForReading(packet, &mock_socket_handler);
258 CHECK(valid);
259 CHECK(client.ReceiveEnableEncryption(packet));
260
263
264 SECTION("Encyption happening client -> server") {
265 CheckEncryption(&client_socket_handler, &server_socket_handler);
266 }
267
268 SECTION("Encyption happening server -> client") {
269 CheckEncryption(&server_socket_handler, &client_socket_handler);
270 }
271
272 SECTION("Unencrypted packet sent causes invalid read packet") {
273 Packet request(&mock_socket_handler, PacketType{});
274 request.Send_uint64(0);
275
276 auto [response, valid] = CreatePacketForReading(request, &client_socket_handler);
277 CHECK(!valid);
278 }
279}
Base class for client side cryptographic authentication handlers.
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.
RequestResult
The processing result of receiving a request.
@ READY_FOR_RESPONSE
We do not have to wait for user input, and can immediately respond to the server.
@ AWAIT_USER_INPUT
We have requested some user input, but must wait on that.
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.
static std::unique_ptr< NetworkAuthenticationServerHandler > Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask=~static_cast< NetworkAuthenticationMethodMask >(0))
Create a NetworkAuthenticationServerHandler.
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.
ResponseResult
The processing result of receiving a response.
@ RETRY_NEXT_METHOD
The client failed to authenticate, but there is another method to try.
@ AUTHENTICATED
The client was authenticated successfully.
@ NOT_AUTHENTICATED
All authentications for this handler have been exhausted.
virtual void SendRequest(struct Packet &p)=0
Create the request to send to the client.
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:188
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 valid
Bits indicating what variable is valid (for each bit, 0 is invalid, 1 is valid).
uint8_t PacketType
Identifier for the packet.
Definition packet.h:21
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:81
Internal entity of a packet.
Definition packet.h:42
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, D destination, Args &&... args)
Transfer data from the packet to the given function.
Definition packet.h:109
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.