OpenTTD Source  20241121-master-g67a0fccfad
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 
13 #include "core/packet.h"
14 
15 #include "../3rdparty/monocypher/monocypher.h"
16 #include "../core/random_func.hpp"
17 #include "../debug.h"
18 #include "../string_func.h"
19 
20 #include "../safeguards.h"
21 
26 static void crypto_wipe(std::span<uint8_t> span)
27 {
28  crypto_wipe(span.data(), span.size());
29 }
30 
33 {
35 }
36 
41 std::span<const uint8_t> X25519DerivedKeys::ClientToServer() const
42 {
43  return std::span(this->keys.data(), X25519_KEY_SIZE);
44 }
45 
50 std::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 
98 private:
99  crypto_aead_ctx context;
100 
101 public:
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 
176 {
177  crypto_wipe(*this);
178 }
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 
190 /* virtual */ void X25519AuthenticationHandler::SendRequest(Packet &p)
191 {
192  p.Send_bytes(this->our_public_key);
194 }
195 
202 {
204  Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
205  return false;
206  }
207 
208  p.Recv_bytes(this->peer_public_key);
210  return true;
211 }
212 
219 bool X25519AuthenticationHandler::SendResponse(Packet &p, std::string_view derived_key_extra_payload)
220 {
221  if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::CLIENT,
222  this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
223  Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
224  return false;
225  }
226 
227  X25519KeyExchangeMessage message;
228  RandomBytesWithFallback(message);
229  X25519Mac mac;
230 
231  crypto_aead_lock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), this->key_exchange_nonce.data(),
232  this->our_public_key.data(), this->our_public_key.size(), message.data(), message.size());
233 
234  p.Send_bytes(this->our_public_key);
235  p.Send_bytes(mac);
236  p.Send_bytes(message);
237  return true;
238 }
239 
245 {
246  return FormatArrayAsHex(this->peer_public_key);
247 }
248 
254 {
255  p.Send_bytes(this->encryption_nonce);
256 }
257 
264 {
265  return p.Recv_bytes(this->encryption_nonce) == this->encryption_nonce.size();
266 }
267 
268 std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateClientToServerEncryptionHandler() const
269 {
270  return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ClientToServer(), this->encryption_nonce);
271 }
272 
273 std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateServerToClientEncryptionHandler() const
274 {
275  return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ServerToClient(), this->encryption_nonce);
276 }
277 
286 {
288  Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
290  }
291 
292  X25519KeyExchangeMessage message{};
293  X25519Mac mac;
294 
295  p.Recv_bytes(this->peer_public_key);
296  p.Recv_bytes(mac);
297  p.Recv_bytes(message);
298 
299  if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::SERVER,
300  this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
301  Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
303  }
304 
305  if (crypto_aead_unlock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), this->key_exchange_nonce.data(),
306  this->peer_public_key.data(), this->peer_public_key.size(), message.data(), message.size()) != 0) {
307  /*
308  * The ciphertext and the message authentication code do not match with the encryption key.
309  * This is most likely an invalid password, or possibly a bug in the client.
310  */
312  }
313 
315 }
316 
317 
319 {
320  bool success = this->X25519AuthenticationHandler::ReceiveRequest(p);
321  if (!success) return NetworkAuthenticationClientHandler::INVALID;
322 
323  this->handler->AskUserForPassword(this->handler);
325 }
326 
334 /* static */ X25519SecretKey X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
335 {
336  X25519SecretKey key{};
337  if (!ConvertHexToBytes(secret_key, key)) {
338  if (secret_key.empty()) {
339  Debug(net, 3, "[crypto] Creating a new random key");
340  } else {
341  Debug(net, 0, "[crypto] Found invalid secret key, creating a new random key");
342  }
344  secret_key = FormatArrayAsHex(key);
345  }
346 
347  public_key = FormatArrayAsHex(key.CreatePublicKey());
348  return key;
349 }
350 
352 {
354  if (result != AUTHENTICATED) return result;
355 
356  std::string peer_public_key = this->GetPeerPublicKey();
357  return this->authorized_key_handler->IsAllowed(peer_public_key) ? AUTHENTICATED : NOT_AUTHENTICATED;
358 }
359 
360 
362 {
364 
365  auto is_of_method = [method](Handler &handler) { return handler->GetAuthenticationMethod() == method; };
366  auto it = std::find_if(handlers.begin(), handlers.end(), is_of_method);
367  if (it == handlers.end()) return INVALID;
368 
369  this->current_handler = it->get();
370 
371  Debug(net, 9, "Received {} authentication request", this->GetName());
372  return this->current_handler->ReceiveRequest(p);
373 }
374 
376 {
377  Debug(net, 9, "Sending {} authentication response", this->GetName());
378 
379  return this->current_handler->SendResponse(p);
380 }
381 
382 /* virtual */ std::string_view CombinedAuthenticationClientHandler::GetName() const
383 {
384  return this->current_handler != nullptr ? this->current_handler->GetName() : "Unknown";
385 }
386 
388 {
390 }
391 
392 
398 {
399  /* Is the handler configured correctly, e.g. does it have a password? */
400  if (!handler->CanBeUsed()) return;
401 
402  this->handlers.push_back(std::move(handler));
403 }
404 
406 {
407  Debug(net, 9, "Sending {} authentication request", this->GetName());
408 
409  p.Send_uint8(this->handlers.back()->GetAuthenticationMethod());
410  this->handlers.back()->SendRequest(p);
411 }
412 
414 {
415  Debug(net, 9, "Receiving {} authentication response", this->GetName());
416 
417  ResponseResult result = this->handlers.back()->ReceiveResponse(p);
418  if (result != NOT_AUTHENTICATED) return result;
419 
420  this->handlers.pop_back();
421  return this->CanBeUsed() ? RETRY_NEXT_METHOD : NOT_AUTHENTICATED;
422 }
423 
424 /* virtual */ std::string_view CombinedAuthenticationServerHandler::GetName() const
425 {
426  return this->CanBeUsed() ? this->handlers.back()->GetName() : "Unknown";
427 }
428 
430 {
431  return this->CanBeUsed() ? this->handlers.back()->GetAuthenticationMethod() : NETWORK_AUTH_METHOD_END;
432 }
433 
435 {
436  return !this->handlers.empty();
437 }
438 
439 
440 /* virtual */ void NetworkAuthenticationPasswordRequestHandler::Reply(const std::string &password)
441 {
442  this->password = password;
443  this->SendResponse();
444 }
445 
451 /* static */ void NetworkAuthenticationClientHandler::EnsureValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
452 {
454 }
455 
462 /* static */ std::unique_ptr<NetworkAuthenticationClientHandler> NetworkAuthenticationClientHandler::Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key)
463 {
464  auto secret = X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(secret_key, public_key);
465  auto handler = std::make_unique<CombinedAuthenticationClientHandler>();
466  handler->Add(std::make_unique<X25519KeyExchangeOnlyClientHandler>(secret));
467  handler->Add(std::make_unique<X25519PAKEClientHandler>(secret, std::move(password_handler)));
468  handler->Add(std::make_unique<X25519AuthorizedKeyClientHandler>(secret));
469  return handler;
470 }
471 
478 std::unique_ptr<NetworkAuthenticationServerHandler> NetworkAuthenticationServerHandler::Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask)
479 {
480  auto secret = X25519SecretKey::CreateRandom();
481  auto handler = std::make_unique<CombinedAuthenticationServerHandler>();
482  if (password_provider != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_PAKE)) {
483  handler->Add(std::make_unique<X25519PAKEServerHandler>(secret, password_provider));
484  }
485 
486  if (authorized_key_handler != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY)) {
487  handler->Add(std::make_unique<X25519AuthorizedKeyServerHandler>(secret, authorized_key_handler));
488  }
489 
490  if (!handler->CanBeUsed() && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY)) {
491  /* Fall back to the plain handler when neither password, nor authorized keys are configured. */
492  handler->Add(std::make_unique<X25519KeyExchangeOnlyServerHandler>(secret));
493  }
494  return handler;
495 }
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
std::vector< Handler > handlers
The handlers that we can authenticate with.
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override
Get the method this handler is providing functionality for.
NetworkAuthenticationClientHandler * current_handler
The currently active handler.
virtual 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.
virtual RequestResult ReceiveRequest(struct Packet &p) override
Read a request from the server.
virtual std::string_view GetName() const override
Get the name of the handler for debug messages.
virtual 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.
virtual ResponseResult ReceiveResponse(struct Packet &p) override
Read the response from the client.
virtual bool CanBeUsed() const override
Checks whether this handler can be used with the current configuration.
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override
Get the method this handler is providing functionality for.
virtual 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.
virtual bool IsAllowed(std::string_view peer_public_key) const =0
Check whether the given public key of the peer is allowed in.
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.
virtual RequestResult ReceiveRequest(struct Packet &p)=0
Read a request from the server.
RequestResult
The processing result of receiving a request.
@ AWAIT_USER_INPUT
We have requested some user input, but must wait on that.
@ INVALID
We have received an invalid request.
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.
virtual std::string_view GetName() const =0
Get the name of the handler for debug messages.
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const =0
Get the method this handler is providing functionality for.
Callback interface for server implementations to provide the current password.
virtual void SendResponse()=0
Callback to trigger sending the response for the password request.
virtual 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=~static_cast< NetworkAuthenticationMethodMask >(0))
Create a NetworkAuthenticationServerHandler.
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.
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.
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...
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.
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.
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.
virtual std::string GetPeerPublicKey() const override
Get the public key the peer provided during the authentication.
virtual ResponseResult ReceiveResponse(struct Packet &p) override
Read the response from the client.
const NetworkAuthenticationAuthorizedKeyHandler * authorized_key_handler
The handler of the authorized keys.
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.
Encryption handler implementation for monocypther encryption after a X25519 key exchange.
~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.
virtual RequestResult ReceiveRequest(struct Packet &p) override
Read a request from the server.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
static void crypto_wipe(std::span< uint8_t > span)
Call crypto_wipe for all the data in the given span.
NetworkAuthenticationMethod
The authentication method that can be used.
@ NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY
No actual authentication is taking place, just perform a x25519 key exchange. This method is not supp...
@ NETWORK_AUTH_METHOD_X25519_PAKE
Authentication using x25519 password-authenticated key agreement.
@ NETWORK_AUTH_METHOD_END
Must ALWAYS be on the end of this list!! (period)
@ NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY
Authentication using x22519 key exchange and authorized keys.
uint16_t NetworkAuthenticationMethodMask
The mask of authentication methods that can be used.
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.
X25519KeyExchangeSide
The side of the key exchange.
@ SERVER
We are the server.
@ CLIENT
We are the client.
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.
Basic functions to create, fill and read packets.
void RandomBytesWithFallback(std::span< uint8_t > buf)
Fill the given buffer with random bytes.
Definition: random_func.cpp:95
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:734
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 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:403
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:197
uint8_t Recv_uint8()
Read a 8 bits integer from the packet.
Definition: packet.cpp:318
void Send_uint8(uint8_t data)
Package a 8 bits integer in the packet.
Definition: packet.cpp:120
size_t RemainingBytesToTransfer() const
Get the amount of bytes that are still available for the Transfer functions.
Definition: packet.cpp:447
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.