OpenTTD Source 20260421-master-gc2fbc6fdeb
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
13#include "core/packet.h"
14
15#include "../3rdparty/monocypher/monocypher.h"
17#include "../debug.h"
18#include "../string_func.h"
19
20#include "../safeguards.h"
21
26static void crypto_wipe(std::span<uint8_t> span)
27{
28 crypto_wipe(span.data(), span.size());
29}
30
36
41std::span<const uint8_t> X25519DerivedKeys::ClientToServer() const
42{
43 return std::span(this->keys.data(), X25519_KEY_SIZE);
44}
45
50std::span<const uint8_t> X25519DerivedKeys::ServerToClient() const
51{
52 return std::span(this->keys.data() + X25519_KEY_SIZE, X25519_KEY_SIZE);
53}
54
65 const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload)
66{
67 X25519Key shared_secret;
68 crypto_x25519(shared_secret.data(), our_secret_key.data(), peer_public_key.data());
69 if (std::all_of(shared_secret.begin(), shared_secret.end(), [](auto v) { return v == 0; })) {
70 /* A shared secret of all zeros means that the peer tried to force the shared secret to a known constant. */
71 return false;
72 }
73
74 crypto_blake2b_ctx ctx;
75 crypto_blake2b_init(&ctx, this->keys.size());
76 crypto_blake2b_update(&ctx, shared_secret.data(), shared_secret.size());
77 switch (side) {
79 crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
80 crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
81 break;
83 crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
84 crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
85 break;
86 default:
87 NOT_REACHED();
88 }
89 crypto_blake2b_update(&ctx, reinterpret_cast<const uint8_t *>(extra_payload.data()), extra_payload.size());
90 crypto_blake2b_final(&ctx, this->keys.data());
91 return true;
92}
93
98private:
99 crypto_aead_ctx context;
100
101public:
107 X25519EncryptionHandler(const std::span<const uint8_t> key, const X25519Nonce &nonce)
108 {
109 assert(key.size() == X25519_KEY_SIZE);
110 crypto_aead_init_x(&this->context, key.data(), nonce.data());
111 }
112
115 {
116 crypto_wipe(&this->context, sizeof(this->context));
117 }
118
119 size_t MACSize() const override
120 {
121 return X25519_MAC_SIZE;
122 }
123
124 bool Decrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
125 {
126 return crypto_aead_read(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size()) == 0;
127 }
128
129 void Encrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
130 {
131 crypto_aead_write(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size());
132 }
133};
134
137{
138 crypto_wipe(*this);
139}
140
146{
147 X25519SecretKey secret_key;
148 RandomBytesWithFallback(secret_key);
149 return secret_key;
150}
151
157{
158 X25519PublicKey public_key;
159 crypto_x25519_public_key(public_key.data(), this->data());
160 return public_key;
161}
162
168{
169 X25519Nonce nonce;
171 return nonce;
172}
173
179
185 our_secret_key(secret_key), our_public_key(secret_key.CreatePublicKey()),
186 key_exchange_nonce(X25519Nonce::CreateRandom()), encryption_nonce(X25519Nonce::CreateRandom())
187{
188}
189
196
203{
205 Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
206 return false;
207 }
208
211 return true;
212}
213
220bool X25519AuthenticationHandler::SendResponse(Packet &p, std::string_view derived_key_extra_payload)
221{
222 if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::CLIENT,
223 this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
224 Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
225 return false;
226 }
227
230 X25519Mac mac;
231
232 crypto_aead_lock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), this->key_exchange_nonce.data(),
233 this->our_public_key.data(), this->our_public_key.size(), message.data(), message.size());
234
235 p.Send_bytes(this->our_public_key);
236 p.Send_bytes(mac);
237 p.Send_bytes(message);
238 return true;
239}
240
249
258
268
270std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateClientToServerEncryptionHandler() const
271{
272 return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ClientToServer(), this->encryption_nonce);
273}
274
276std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateServerToClientEncryptionHandler() const
277{
278 return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ServerToClient(), this->encryption_nonce);
279}
280
289{
291 Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
293 }
294
295 X25519KeyExchangeMessage message{};
296 X25519Mac mac;
297
299 p.Recv_bytes(mac);
300 p.Recv_bytes(message);
301
302 if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::SERVER,
303 this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
304 Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
306 }
307
308 if (crypto_aead_unlock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), this->key_exchange_nonce.data(),
309 this->peer_public_key.data(), this->peer_public_key.size(), message.data(), message.size()) != 0) {
310 /*
311 * The ciphertext and the message authentication code do not match with the encryption key.
312 * This is most likely an invalid password, or possibly a bug in the client.
313 */
315 }
316
318}
319
320
329
337/* static */ X25519SecretKey X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
338{
339 X25519SecretKey key{};
340 if (!ConvertHexToBytes(secret_key, key)) {
341 if (secret_key.empty()) {
342 Debug(net, 3, "[crypto] Creating a new random key");
343 } else {
344 Debug(net, 0, "[crypto] Found invalid secret key, creating a new random key");
345 }
347 secret_key = FormatArrayAsHex(key);
348 }
349
350 public_key = FormatArrayAsHex(key.CreatePublicKey());
351 return key;
352}
353
362
363
365{
367
368 auto is_of_method = [method](Handler &handler) { return handler->GetAuthenticationMethod() == method; };
369 auto it = std::ranges::find_if(handlers, is_of_method);
370 if (it == handlers.end()) return RequestResult::Invalid;
371
372 this->current_handler = it->get();
373
374 Debug(net, 9, "Received {} authentication request", this->GetName());
375 return this->current_handler->ReceiveRequest(p);
376}
377
379{
380 Debug(net, 9, "Sending {} authentication response", this->GetName());
381
382 return this->current_handler->SendResponse(p);
383}
384
385/* virtual */ std::string_view CombinedAuthenticationClientHandler::GetName() const
386{
387 return this->current_handler != nullptr ? this->current_handler->GetName() : "Unknown";
388}
389
391{
392 return this->current_handler != nullptr ? this->current_handler->GetAuthenticationMethod() : NetworkAuthenticationMethod::End;
393}
394
395
401{
402 /* Is the handler configured correctly, e.g. does it have a password? */
403 if (!handler->CanBeUsed()) return;
404
405 this->handlers.push_back(std::move(handler));
406}
407
409{
410 Debug(net, 9, "Sending {} authentication request", this->GetName());
411
412 p.Send_uint8(to_underlying(this->handlers.back()->GetAuthenticationMethod()));
413 this->handlers.back()->SendRequest(p);
414}
415
417{
418 Debug(net, 9, "Receiving {} authentication response", this->GetName());
419
420 ResponseResult result = this->handlers.back()->ReceiveResponse(p);
421 if (result != ResponseResult::NotAuthenticated) return result;
422
423 this->handlers.pop_back();
425}
426
427/* virtual */ std::string_view CombinedAuthenticationServerHandler::GetName() const
428{
429 return this->CanBeUsed() ? this->handlers.back()->GetName() : "Unknown";
430}
431
433{
434 return this->CanBeUsed() ? this->handlers.back()->GetAuthenticationMethod() : NetworkAuthenticationMethod::End;
435}
436
438{
439 return !this->handlers.empty();
440}
441
442
444{
445 this->password = password;
446 this->SendResponse();
447}
448
454/* static */ void NetworkAuthenticationClientHandler::EnsureValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
455{
457}
458
466/* static */ std::unique_ptr<NetworkAuthenticationClientHandler> NetworkAuthenticationClientHandler::Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key)
467{
469 auto handler = std::make_unique<CombinedAuthenticationClientHandler>();
470 handler->Add(std::make_unique<X25519KeyExchangeOnlyClientHandler>(secret));
471 handler->Add(std::make_unique<X25519PAKEClientHandler>(secret, std::move(password_handler)));
472 handler->Add(std::make_unique<X25519AuthorizedKeyClientHandler>(secret));
473 return handler;
474}
475
483std::unique_ptr<NetworkAuthenticationServerHandler> NetworkAuthenticationServerHandler::Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask)
484{
485 auto secret = X25519SecretKey::CreateRandom();
486 auto handler = std::make_unique<CombinedAuthenticationServerHandler>();
487 if (password_provider != nullptr && client_supported_method_mask.Test(NetworkAuthenticationMethod::X25519_PAKE)) {
488 handler->Add(std::make_unique<X25519PAKEServerHandler>(secret, password_provider));
489 }
490
491 if (authorized_key_handler != nullptr && client_supported_method_mask.Test(NetworkAuthenticationMethod::X25519_AuthorizedKey)) {
492 handler->Add(std::make_unique<X25519AuthorizedKeyServerHandler>(secret, authorized_key_handler));
493 }
494
495 if (!handler->CanBeUsed() && client_supported_method_mask.Test(NetworkAuthenticationMethod::X25519_KeyExchangeOnly)) {
496 /* Fall back to the plain handler when neither password, nor authorized keys are configured. */
497 handler->Add(std::make_unique<X25519KeyExchangeOnlyServerHandler>(secret));
498 }
499 return handler;
500}
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
std::vector< Handler > handlers
The handlers that we can authenticate with.
NetworkAuthenticationMethod GetAuthenticationMethod() const override
Get the method this handler is providing functionality for.
NetworkAuthenticationClientHandler * current_handler
The currently active handler.
bool SendResponse(struct Packet &p) override
Create the response to send to the server.
std::unique_ptr< NetworkAuthenticationClientHandler > Handler
The type of the inner handlers.
RequestResult ReceiveRequest(struct Packet &p) override
Read a request from the server.
std::string_view GetName() const override
Get the name of the handler for debug messages.
std::string_view GetName() const override
Get the name of the handler for debug messages.
void Add(Handler &&handler)
Add the given sub-handler to this handler, if the handler can be used (e.g.
std::vector< Handler > handlers
The handlers that we can (still) authenticate with.
std::unique_ptr< NetworkAuthenticationServerHandler > Handler
The type of the inner handlers.
ResponseResult ReceiveResponse(struct Packet &p) override
Read the response from the client.
bool CanBeUsed() const override
Checks whether this handler can be used with the current configuration.
NetworkAuthenticationMethod GetAuthenticationMethod() const override
Get the method this handler is providing functionality for.
void SendRequest(struct Packet &p) override
Create the request to send to the client.
Callback interface for server implementations to provide the authorized key validation.
RequestResult
The processing result of receiving a request.
@ AwaitUserInput
We have requested some user input, but must wait on that.
@ Invalid
We have received an invalid request.
static void EnsureValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
Ensures that the given secret key is valid, and when not overwrite it with a valid secret key.
static std::unique_ptr< NetworkAuthenticationClientHandler > Create(std::shared_ptr< NetworkAuthenticationPasswordRequestHandler > password_handler, std::string &secret_key, std::string &public_key)
Create a NetworkAuthenticationClientHandler.
Callback interface for server implementations to provide the current password.
virtual void SendResponse()=0
Callback to trigger sending the response for the password request.
void Reply(const std::string &password) override
Reply to the request with the given password.
std::string password
The entered password.
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.
Base class for handling the encryption (or decryption) of a network connection.
bool ReceiveEnableEncryption(struct Packet &p)
Receive the initial nonce for the encrypted connection.
X25519SecretKey our_secret_key
The secret key used by us.
X25519AuthenticationHandler(const X25519SecretKey &secret_key)
Create the handler, and generate the public keys accordingly.
X25519PublicKey peer_public_key
The public key used by our peer.
X25519Nonce encryption_nonce
The nonce to prevent replay attacks the encrypted connection.
NetworkAuthenticationServerHandler::ResponseResult ReceiveResponse(struct Packet &p, std::string_view derived_key_extra_payload)
Read the key exchange data from a Packet that came from the client, and check whether the client pass...
std::unique_ptr< NetworkEncryptionHandler > CreateClientToServerEncryptionHandler() const
Create a NetworkEncryptionHandler to encrypt or decrypt messages from the client to the server.
X25519Nonce key_exchange_nonce
The nonce to prevent replay attacks of the key exchange.
bool SendResponse(struct Packet &p, std::string_view derived_key_extra_payload)
Perform the key exchange, and when that is correct fill the Packet with the appropriate data.
X25519DerivedKeys derived_keys
Keys derived from the authentication process.
std::string GetPeerPublicKey() const
Get the public key the peer provided for the key exchange.
void SendRequest(struct Packet &p)
Create the request to send to the client.
bool ReceiveRequest(struct Packet &p)
Read the key exchange data from a Packet that came from the server,.
void SendEnableEncryption(struct Packet &p) const
Send the initial nonce for the encrypted connection.
std::unique_ptr< NetworkEncryptionHandler > CreateServerToClientEncryptionHandler() const
Create a NetworkEncryptionHandler to encrypt or decrypt messages from the server to the client.
X25519PublicKey our_public_key
The public key used by us.
static X25519SecretKey GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
Get the secret key from the given string.
ResponseResult ReceiveResponse(struct Packet &p) override
Read the response from the client.
const NetworkAuthenticationAuthorizedKeyHandler * authorized_key_handler
The handler of the authorized keys.
std::string GetPeerPublicKey() const override
Get the public key the peer provided during the authentication.
std::array< uint8_t, X25519_KEY_SIZE+X25519_KEY_SIZE > keys
Single contiguous buffer to store the derived keys in, as they are generated as a single hash.
bool Exchange(const X25519PublicKey &peer_public_key, X25519KeyExchangeSide side, const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload)
Perform the actual key exchange.
std::span< const uint8_t > ClientToServer() const
Get the key to encrypt or decrypt a message sent from the client to the server.
std::span< const uint8_t > ServerToClient() const
Get the key to encrypt or decrypt a message sent from the server to the client.
~X25519DerivedKeys()
Ensure the derived keys do not get leaked when we're done with it.
~X25519EncryptionHandler()
Ensure the encryption context is wiped!
size_t MACSize() const override
Get the size of the MAC (Message Authentication Code) used by the underlying encryption protocol.
void Encrypt(std::span< std::uint8_t > mac, std::span< std::uint8_t > message) override
Encrypt the given message in-place, and write the associated MAC.
crypto_aead_ctx context
The actual encryption context.
bool Decrypt(std::span< std::uint8_t > mac, std::span< std::uint8_t > message) override
Decrypt the given message in-place, validating against the given MAC.
X25519EncryptionHandler(const std::span< const uint8_t > key, const X25519Nonce &nonce)
Create the encryption handler.
std::shared_ptr< NetworkAuthenticationPasswordRequestHandler > handler
Callback to get the password from if needed.
RequestResult ReceiveRequest(struct Packet &p) override
Read a request from the server.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
static void crypto_wipe(std::span< uint8_t > span)
Call crypto_wipe for all the data in the given span.
EnumBitSet< NetworkAuthenticationMethod, uint16_t > NetworkAuthenticationMethodMask
The mask of authentication methods that can be used.
NetworkAuthenticationMethod
The authentication method that can be used.
@ X25519_KeyExchangeOnly
No actual authentication is taking place, just perform a x25519 key exchange. This method is not supp...
@ X25519_PAKE
Authentication using x25519 password-authenticated key agreement.
@ End
Must ALWAYS be on the end of this list!! (period).
@ X25519_AuthorizedKey
Authentication using x22519 key exchange and authorized keys.
Internal bits to the crypto of the network handling.
constexpr size_t X25519_MAC_SIZE
The number of bytes the message authentication codes are in X25519.
constexpr size_t X25519_NONCE_SIZE
The number of bytes the nonces are in X25519.
std::array< uint8_t, X25519_KEY_EXCHANGE_MESSAGE_SIZE > X25519KeyExchangeMessage
Container for a X25519 key exchange message.
constexpr size_t X25519_KEY_EXCHANGE_MESSAGE_SIZE
The number of bytes the (random) payload of the authentication message has.
constexpr size_t X25519_KEY_SIZE
The number of bytes the public and secret keys are in X25519.
std::array< uint8_t, X25519_MAC_SIZE > X25519Mac
Container for a X25519 message authentication code.
X25519KeyExchangeSide
The side of the key exchange.
Basic functions to create, fill and read packets.
void RandomBytesWithFallback(std::span< uint8_t > buf)
Fill the given buffer with random bytes.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
bool ConvertHexToBytes(std::string_view hex, std::span< uint8_t > bytes)
Convert a hex-string to a byte-array, while validating it was actually hex.
Definition string.cpp:572
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:56
size_t Recv_bytes(std::span< uint8_t > span)
Extract at most the length of the span bytes from the packet into the span.
Definition packet.cpp:401
std::span< const uint8_t > Send_bytes(const std::span< const uint8_t > span)
Send as many of the bytes as possible in the packet.
Definition packet.cpp:195
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
Definition packet.cpp:316
void Send_uint8(uint8_t data)
Package a 8 bits integer in the packet.
Definition packet.cpp:118
size_t RemainingBytesToTransfer() const
Get the amount of bytes that are still available for the Transfer functions.
Definition packet.cpp:445
Container for a X25519 key that is automatically crypto-wiped when destructed.
~X25519Key()
Ensure the key does not get leaked when we're done with it.
Container for a X25519 nonce that is automatically crypto-wiped when destructed.
static X25519Nonce CreateRandom()
Create a new nonce that's filled with random bytes.
~X25519Nonce()
Ensure the nonce does not get leaked when we're done with it.
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.