mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-16 00:12:51 +00:00
Codechange: add set of classes providing authentication and encryption
This commit is contained in:
parent
88cf99017a
commit
fb9d4afa5c
@ -16,6 +16,9 @@ add_files(
|
|||||||
network_content_gui.h
|
network_content_gui.h
|
||||||
network_coordinator.cpp
|
network_coordinator.cpp
|
||||||
network_coordinator.h
|
network_coordinator.h
|
||||||
|
network_crypto.cpp
|
||||||
|
network_crypto.h
|
||||||
|
network_crypto_internal.h
|
||||||
network_func.h
|
network_func.h
|
||||||
network_gamelist.cpp
|
network_gamelist.cpp
|
||||||
network_gamelist.h
|
network_gamelist.h
|
||||||
|
474
src/network/network_crypto.cpp
Normal file
474
src/network/network_crypto.cpp
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file network_crypto.cpp Implementation of the network specific cryptography helpers. */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
|
||||||
|
#include "network_crypto_internal.h"
|
||||||
|
#include "core/packet.h"
|
||||||
|
|
||||||
|
#include "../3rdparty/monocypher/monocypher.h"
|
||||||
|
#include "../core/random_func.hpp"
|
||||||
|
#include "../debug.h"
|
||||||
|
#include "../string_func.h"
|
||||||
|
|
||||||
|
#include "../safeguards.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call \c crypto_wipe for all the data in the given span.
|
||||||
|
* @param span The span to cryptographically wipe.
|
||||||
|
*/
|
||||||
|
static void crypto_wipe(std::span<uint8_t> span)
|
||||||
|
{
|
||||||
|
crypto_wipe(span.data(), span.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure the derived keys do not get leaked when we're done with it. */
|
||||||
|
X25519DerivedKeys::~X25519DerivedKeys()
|
||||||
|
{
|
||||||
|
crypto_wipe(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key to encrypt or decrypt a message sent from the client to the server.
|
||||||
|
* @return The raw bytes of the key.
|
||||||
|
*/
|
||||||
|
std::span<const uint8_t> X25519DerivedKeys::ClientToServer() const
|
||||||
|
{
|
||||||
|
return std::span(this->keys.data(), X25519_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key to encrypt or decrypt a message sent from the server to the client.
|
||||||
|
* @return The raw bytes of the key.
|
||||||
|
*/
|
||||||
|
std::span<const uint8_t> X25519DerivedKeys::ServerToClient() const
|
||||||
|
{
|
||||||
|
return std::span(this->keys.data() + X25519_KEY_SIZE, X25519_KEY_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the actual key exchange.
|
||||||
|
* @param peer_public_key The public key chosen by the other participant of the key exchange.
|
||||||
|
* @param side Whether we are the client or server; used to hash the public key of us and the peer in the right order.
|
||||||
|
* @param our_secret_key The secret key of us.
|
||||||
|
* @param our_public_key The public key of us.
|
||||||
|
* @param extra_payload Extra payload to put into the hash function to create the derived keys.
|
||||||
|
* @return True when the key exchange has succeeded, false when an illegal public key was given.
|
||||||
|
*/
|
||||||
|
bool X25519DerivedKeys::Exchange(const X25519PublicKey &peer_public_key, X25519KeyExchangeSide side,
|
||||||
|
const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload)
|
||||||
|
{
|
||||||
|
X25519Key shared_secret;
|
||||||
|
crypto_x25519(shared_secret.data(), our_secret_key.data(), peer_public_key.data());
|
||||||
|
if (std::all_of(shared_secret.begin(), shared_secret.end(), [](auto v) { return v == 0; })) {
|
||||||
|
/* A shared secret of all zeros means that the peer tried to force the shared secret to a known constant. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto_blake2b_ctx ctx;
|
||||||
|
crypto_blake2b_init(&ctx, this->keys.size());
|
||||||
|
crypto_blake2b_update(&ctx, shared_secret.data(), shared_secret.size());
|
||||||
|
switch (side) {
|
||||||
|
case X25519KeyExchangeSide::SERVER:
|
||||||
|
crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
|
||||||
|
crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
|
||||||
|
break;
|
||||||
|
case X25519KeyExchangeSide::CLIENT:
|
||||||
|
crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
|
||||||
|
crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NOT_REACHED();
|
||||||
|
}
|
||||||
|
crypto_blake2b_update(&ctx, reinterpret_cast<const uint8_t *>(extra_payload.data()), extra_payload.size());
|
||||||
|
crypto_blake2b_final(&ctx, this->keys.data());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption handler implementation for monocypther encryption after a X25519 key exchange.
|
||||||
|
*/
|
||||||
|
class X25519EncryptionHandler : public NetworkEncryptionHandler {
|
||||||
|
private:
|
||||||
|
crypto_aead_ctx context; ///< The actual encryption context.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the encryption handler.
|
||||||
|
* @param key The key used for the encryption.
|
||||||
|
* @param nonce The nonce used for the encryption.
|
||||||
|
*/
|
||||||
|
X25519EncryptionHandler(const std::span<const uint8_t> key, const X25519Nonce &nonce)
|
||||||
|
{
|
||||||
|
assert(key.size() == X25519_KEY_SIZE);
|
||||||
|
crypto_aead_init_x(&this->context, key.data(), nonce.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure the encryption context is wiped! */
|
||||||
|
~X25519EncryptionHandler()
|
||||||
|
{
|
||||||
|
crypto_wipe(&this->context, sizeof(this->context));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MACSize() const override
|
||||||
|
{
|
||||||
|
return X25519_MAC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Decrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
|
||||||
|
{
|
||||||
|
return crypto_aead_read(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Encrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
|
||||||
|
{
|
||||||
|
crypto_aead_write(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Ensure the key does not get leaked when we're done with it. */
|
||||||
|
X25519Key::~X25519Key()
|
||||||
|
{
|
||||||
|
crypto_wipe(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new secret key that's filled with random bytes.
|
||||||
|
* @return The randomly filled key.
|
||||||
|
*/
|
||||||
|
/* static */ X25519SecretKey X25519SecretKey::CreateRandom()
|
||||||
|
{
|
||||||
|
X25519SecretKey secret_key;
|
||||||
|
RandomBytesWithFallback(secret_key);
|
||||||
|
return secret_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the public key associated with this secret key.
|
||||||
|
* @return The public key.
|
||||||
|
*/
|
||||||
|
X25519PublicKey X25519SecretKey::CreatePublicKey() const
|
||||||
|
{
|
||||||
|
X25519PublicKey public_key;
|
||||||
|
crypto_x25519_public_key(public_key.data(), this->data());
|
||||||
|
return public_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new nonce that's filled with random bytes.
|
||||||
|
* @return The randomly filled nonce.
|
||||||
|
*/
|
||||||
|
/* static */ X25519Nonce X25519Nonce::CreateRandom()
|
||||||
|
{
|
||||||
|
X25519Nonce nonce;
|
||||||
|
RandomBytesWithFallback(nonce);
|
||||||
|
return nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure the nonce does not get leaked when we're done with it. */
|
||||||
|
X25519Nonce::~X25519Nonce()
|
||||||
|
{
|
||||||
|
crypto_wipe(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the handler, and generate the public keys accordingly.
|
||||||
|
* @param secret_key The secret key to use for this handler. Defaults to secure random data.
|
||||||
|
*/
|
||||||
|
X25519AuthenticationHandler::X25519AuthenticationHandler(const X25519SecretKey &secret_key) :
|
||||||
|
our_secret_key(secret_key), our_public_key(secret_key.CreatePublicKey()), nonce(X25519Nonce::CreateRandom())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ void X25519AuthenticationHandler::SendRequest(Packet &p)
|
||||||
|
{
|
||||||
|
p.Send_bytes(this->our_public_key);
|
||||||
|
p.Send_bytes(this->nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the key exchange data from a \c Packet that came from the server,
|
||||||
|
* @param p The packet that has been received.
|
||||||
|
* @return True when the data seems correct.
|
||||||
|
*/
|
||||||
|
bool X25519AuthenticationHandler::ReceiveRequest(Packet &p)
|
||||||
|
{
|
||||||
|
if (p.RemainingBytesToTransfer() != X25519_KEY_SIZE + X25519_NONCE_SIZE) {
|
||||||
|
Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Recv_bytes(this->peer_public_key);
|
||||||
|
p.Recv_bytes(this->nonce);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the key exchange, and when that is correct fill the \c Packet with the appropriate data.
|
||||||
|
* @param p The packet that has to be sent.
|
||||||
|
* @param derived_key_extra_payload The extra payload to pass to the key exchange.
|
||||||
|
* @return Whether the key exchange was successful or not.
|
||||||
|
*/
|
||||||
|
bool X25519AuthenticationHandler::SendResponse(Packet &p, std::string_view derived_key_extra_payload)
|
||||||
|
{
|
||||||
|
if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::CLIENT,
|
||||||
|
this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
|
||||||
|
Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
X25519KeyExchangeMessage message;
|
||||||
|
RandomBytesWithFallback(message);
|
||||||
|
X25519Mac mac;
|
||||||
|
|
||||||
|
crypto_aead_lock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), nonce.data(),
|
||||||
|
this->our_public_key.data(), this->our_public_key.size(), message.data(), message.size());
|
||||||
|
|
||||||
|
p.Send_bytes(this->our_public_key);
|
||||||
|
p.Send_bytes(mac);
|
||||||
|
p.Send_bytes(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the public key the peer provided for the key exchange.
|
||||||
|
* @return The hexadecimal string representation of the peer's public key.
|
||||||
|
*/
|
||||||
|
std::string X25519AuthenticationHandler::GetPeerPublicKey() const
|
||||||
|
{
|
||||||
|
return FormatArrayAsHex(this->peer_public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateClientToServerEncryptionHandler() const
|
||||||
|
{
|
||||||
|
return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ClientToServer(), this->nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateServerToClientEncryptionHandler() const
|
||||||
|
{
|
||||||
|
return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ServerToClient(), this->nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the key exchange data from a \c Packet that came from the client, and check whether the client passes the key
|
||||||
|
* exchange successfully.
|
||||||
|
* @param p The packet that has been received.
|
||||||
|
* @param derived_key_extra_payload The extra payload to pass to the key exchange.
|
||||||
|
* @return Whether the authentication was successful or not.
|
||||||
|
*/
|
||||||
|
NetworkAuthenticationServerHandler::ResponseResult X25519AuthenticationHandler::ReceiveResponse(Packet &p, std::string_view derived_key_extra_payload)
|
||||||
|
{
|
||||||
|
if (p.RemainingBytesToTransfer() != X25519_KEY_SIZE + X25519_MAC_SIZE + X25519_KEY_EXCHANGE_MESSAGE_SIZE) {
|
||||||
|
Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
|
||||||
|
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
X25519KeyExchangeMessage message{};
|
||||||
|
X25519Mac mac;
|
||||||
|
|
||||||
|
p.Recv_bytes(this->peer_public_key);
|
||||||
|
p.Recv_bytes(mac);
|
||||||
|
p.Recv_bytes(message);
|
||||||
|
|
||||||
|
if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::SERVER,
|
||||||
|
this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
|
||||||
|
Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
|
||||||
|
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crypto_aead_unlock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), nonce.data(),
|
||||||
|
this->peer_public_key.data(), this->peer_public_key.size(), message.data(), message.size()) != 0) {
|
||||||
|
/*
|
||||||
|
* The ciphertext and the message authentication code do not match with the encryption key.
|
||||||
|
* This is most likely an invalid password, or possibly a bug in the client.
|
||||||
|
*/
|
||||||
|
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkAuthenticationServerHandler::AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationClientHandler::RequestResult X25519PAKEClientHandler::ReceiveRequest(struct Packet &p)
|
||||||
|
{
|
||||||
|
bool success = this->X25519AuthenticationHandler::ReceiveRequest(p);
|
||||||
|
if (!success) return NetworkAuthenticationClientHandler::INVALID;
|
||||||
|
|
||||||
|
this->handler->AskUserForPassword(this->handler);
|
||||||
|
return NetworkAuthenticationClientHandler::AWAIT_USER_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the secret key from the given string. If that is not a valid secret key, reset it with a random one.
|
||||||
|
* Furthermore update the public key so it is always in sync with the private key.
|
||||||
|
* @param secret_key The secret key to read/validate/fix.
|
||||||
|
* @param public_key The public key to update.
|
||||||
|
* @return The valid secret key.
|
||||||
|
*/
|
||||||
|
/* static */ X25519SecretKey X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
|
||||||
|
{
|
||||||
|
X25519SecretKey key{};
|
||||||
|
if (!ConvertHexToBytes(secret_key, key)) {
|
||||||
|
if (secret_key.empty()) {
|
||||||
|
Debug(net, 3, "[crypto] Creating a new random key");
|
||||||
|
} else {
|
||||||
|
Debug(net, 0, "[crypto] Found invalid secret key, creating a new random key");
|
||||||
|
}
|
||||||
|
key = X25519SecretKey::CreateRandom();
|
||||||
|
secret_key = FormatArrayAsHex(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public_key = FormatArrayAsHex(key.CreatePublicKey());
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationServerHandler::ResponseResult X25519AuthorizedKeyServerHandler::ReceiveResponse(Packet &p)
|
||||||
|
{
|
||||||
|
ResponseResult result = this->X25519AuthenticationHandler::ReceiveResponse(p, {});
|
||||||
|
if (result != AUTHENTICATED) return result;
|
||||||
|
|
||||||
|
std::string peer_public_key = this->GetPeerPublicKey();
|
||||||
|
return this->authorized_key_handler->IsAllowed(peer_public_key) ? AUTHENTICATED : NOT_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationClientHandler::RequestResult CombinedAuthenticationClientHandler::ReceiveRequest(struct Packet &p)
|
||||||
|
{
|
||||||
|
NetworkAuthenticationMethod method = static_cast<NetworkAuthenticationMethod>(p.Recv_uint8());
|
||||||
|
|
||||||
|
auto is_of_method = [method](Handler &handler) { return handler->GetAuthenticationMethod() == method; };
|
||||||
|
auto it = std::find_if(handlers.begin(), handlers.end(), is_of_method);
|
||||||
|
if (it == handlers.end()) return INVALID;
|
||||||
|
|
||||||
|
this->current_handler = it->get();
|
||||||
|
|
||||||
|
Debug(net, 9, "Received {} authentication request", this->GetName());
|
||||||
|
return this->current_handler->ReceiveRequest(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ bool CombinedAuthenticationClientHandler::SendResponse(struct Packet &p)
|
||||||
|
{
|
||||||
|
Debug(net, 9, "Sending {} authentication response", this->GetName());
|
||||||
|
|
||||||
|
return this->current_handler->SendResponse(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ std::string_view CombinedAuthenticationClientHandler::GetName() const
|
||||||
|
{
|
||||||
|
return this->current_handler != nullptr ? this->current_handler->GetName() : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationMethod CombinedAuthenticationClientHandler::GetAuthenticationMethod() const
|
||||||
|
{
|
||||||
|
return this->current_handler != nullptr ? this->current_handler->GetAuthenticationMethod() : NETWORK_AUTH_METHOD_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given sub-handler to this handler, if the handler can be used (e.g. there are authorized keys or there is a password).
|
||||||
|
* @param handler The handler to add.
|
||||||
|
*/
|
||||||
|
void CombinedAuthenticationServerHandler::Add(CombinedAuthenticationServerHandler::Handler &&handler)
|
||||||
|
{
|
||||||
|
/* Is the handler configured correctly, e.g. does it have a password? */
|
||||||
|
if (!handler->CanBeUsed()) return;
|
||||||
|
|
||||||
|
this->handlers.push_back(std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ void CombinedAuthenticationServerHandler::SendRequest(struct Packet &p)
|
||||||
|
{
|
||||||
|
Debug(net, 9, "Sending {} authentication request", this->GetName());
|
||||||
|
|
||||||
|
p.Send_uint8(this->handlers.back()->GetAuthenticationMethod());
|
||||||
|
this->handlers.back()->SendRequest(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationServerHandler::ResponseResult CombinedAuthenticationServerHandler::ReceiveResponse(struct Packet &p)
|
||||||
|
{
|
||||||
|
Debug(net, 9, "Receiving {} authentication response", this->GetName());
|
||||||
|
|
||||||
|
ResponseResult result = this->handlers.back()->ReceiveResponse(p);
|
||||||
|
if (result != NOT_AUTHENTICATED) return result;
|
||||||
|
|
||||||
|
this->handlers.pop_back();
|
||||||
|
return this->CanBeUsed() ? RETRY_NEXT_METHOD : NOT_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ std::string_view CombinedAuthenticationServerHandler::GetName() const
|
||||||
|
{
|
||||||
|
return this->CanBeUsed() ? this->handlers.back()->GetName() : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ NetworkAuthenticationMethod CombinedAuthenticationServerHandler::GetAuthenticationMethod() const
|
||||||
|
{
|
||||||
|
return this->CanBeUsed() ? this->handlers.back()->GetAuthenticationMethod() : NETWORK_AUTH_METHOD_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ bool CombinedAuthenticationServerHandler::CanBeUsed() const
|
||||||
|
{
|
||||||
|
return !this->handlers.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* virtual */ void NetworkAuthenticationPasswordRequestHandler::Reply(const std::string &password)
|
||||||
|
{
|
||||||
|
this->password = password;
|
||||||
|
this->SendResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual */ bool NetworkAuthenticationDefaultAuthorizedKeyHandler::IsAllowed(std::string_view peer_public_key) const
|
||||||
|
{
|
||||||
|
for (const auto &allowed : *this->authorized_keys) {
|
||||||
|
if (StrEqualsIgnoreCase(allowed, peer_public_key)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a NetworkAuthenticationClientHandler.
|
||||||
|
* @param password_handler The handler for when a request for password needs to be passed on to the user.
|
||||||
|
* @param secret_key The location where the secret key is stored; can be overwritten when invalid.
|
||||||
|
* @param public_key The location where the public key is stored; can be overwritten when invalid.
|
||||||
|
*/
|
||||||
|
/* static */ std::unique_ptr<NetworkAuthenticationClientHandler> NetworkAuthenticationClientHandler::Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key)
|
||||||
|
{
|
||||||
|
auto secret = X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(secret_key, public_key);
|
||||||
|
auto handler = std::make_unique<CombinedAuthenticationClientHandler>();
|
||||||
|
handler->Add(std::make_unique<X25519KeyExchangeOnlyClientHandler>(secret));
|
||||||
|
handler->Add(std::make_unique<X25519PAKEClientHandler>(secret, std::move(password_handler)));
|
||||||
|
handler->Add(std::make_unique<X25519AuthorizedKeyClientHandler>(secret));
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a NetworkAuthenticationServerHandler.
|
||||||
|
* @param password_provider Callback to provide the password handling. Must remain valid until the authentication has succeeded or failed. Can be \c nullptr to skip password checks.
|
||||||
|
* @param authorized_key_handler Callback to provide the authorized key handling. Must remain valid until the authentication has succeeded or failed. Can be \c nullptr to skip authorized key checks.
|
||||||
|
* @param client_supported_method_mask Bitmask of the methods that are supported by the client. Defaults to support of all methods.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<NetworkAuthenticationServerHandler> NetworkAuthenticationServerHandler::Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask)
|
||||||
|
{
|
||||||
|
auto secret = X25519SecretKey::CreateRandom();
|
||||||
|
auto handler = std::make_unique<CombinedAuthenticationServerHandler>();
|
||||||
|
if (password_provider != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_PAKE)) {
|
||||||
|
handler->Add(std::make_unique<X25519PAKEServerHandler>(secret, password_provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authorized_key_handler != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY)) {
|
||||||
|
handler->Add(std::make_unique<X25519AuthorizedKeyServerHandler>(secret, authorized_key_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler->CanBeUsed() && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY)) {
|
||||||
|
/* Fall back to the plain handler when neither password, nor authorized keys are configured. */
|
||||||
|
handler->Add(std::make_unique<X25519KeyExchangeOnlyServerHandler>(secret));
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
287
src/network/network_crypto.h
Normal file
287
src/network/network_crypto.h
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file network_crypto.h Crypto specific bits of the network handling.
|
||||||
|
*
|
||||||
|
* This provides a set of functionality to perform authentication combined with a key exchange,
|
||||||
|
* to create a shared secret as well as encryption using those shared secrets.
|
||||||
|
*
|
||||||
|
* For the authentication/key exchange, the server determines the available methods and creates
|
||||||
|
* the appropriate \c NetworkAuthenticationServerHandler. This will be used to create a request
|
||||||
|
* for the client, which instantiates a \c NetworkAuthenticationClientHandler to handle that
|
||||||
|
* request.
|
||||||
|
* At the moment there are three types of request: key exchange only, password-authenticated key
|
||||||
|
* exchange (PAKE) and authorized keys. When the request is for a password, the user is asked
|
||||||
|
* for the password via an essentially asynchronous callback from the client handler. For the
|
||||||
|
* other requests no input from the user is needed, and these are immediately ready to generate
|
||||||
|
* the response for the server.
|
||||||
|
*
|
||||||
|
* The server will validate the response resulting in either the user being authenticated or not.
|
||||||
|
* When the user failed authentication, there might be a possibility to retry. For example when
|
||||||
|
* the server has configured authorized keys and passwords; when the client fails with the
|
||||||
|
* authorized keys, it will retry with the password.
|
||||||
|
*
|
||||||
|
* Once the key exchange/authentication has been done, the server can signal the client to
|
||||||
|
* upgrade the network connection to use encryption using the shared secret of the key exchange.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETWORK_CRYPTO_H
|
||||||
|
#define NETWORK_CRYPTO_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for handling the encryption (or decryption) of a network connection.
|
||||||
|
*/
|
||||||
|
class NetworkEncryptionHandler {
|
||||||
|
public:
|
||||||
|
virtual ~NetworkEncryptionHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the MAC (Message Authentication Code) used by the underlying encryption protocol.
|
||||||
|
* @return The size, in bytes, of the MACs.
|
||||||
|
*/
|
||||||
|
virtual size_t MACSize() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the given message in-place, validating against the given MAC.
|
||||||
|
* @param mac The message authentication code (MAC).
|
||||||
|
* @param message The location of the message to decrypt.
|
||||||
|
* @return Whether decryption and authentication/validation of the message succeeded.
|
||||||
|
*/
|
||||||
|
virtual bool Decrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the given message in-place, and write the associated MAC.
|
||||||
|
* @param mac The location to write the message authentication code (MAC) to.
|
||||||
|
* @param message The location of the message to encrypt.
|
||||||
|
*/
|
||||||
|
virtual void Encrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for requests for passwords in the context of network authentication.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationPasswordRequest {
|
||||||
|
public:
|
||||||
|
virtual ~NetworkAuthenticationPasswordRequest() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reply to the request with the given password.
|
||||||
|
*/
|
||||||
|
virtual void Reply(const std::string &password) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for client implementations to provide the handling of the password requests.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationPasswordRequestHandler : public NetworkAuthenticationPasswordRequest {
|
||||||
|
protected:
|
||||||
|
friend class X25519PAKEClientHandler;
|
||||||
|
|
||||||
|
std::string password; ///< The entered password.
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void Reply(const std::string &password) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to trigger sending the response for the password request.
|
||||||
|
*/
|
||||||
|
virtual void SendResponse() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to trigger asking the user for the password.
|
||||||
|
* @param request The request to the user, to which it can reply with the password.
|
||||||
|
*/
|
||||||
|
virtual void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for server implementations to provide the current password.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationPasswordProvider {
|
||||||
|
public:
|
||||||
|
virtual ~NetworkAuthenticationPasswordProvider() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to return the password where to validate against.
|
||||||
|
* @return \c std::string_view of the current password; an empty view means no password check will be performed.
|
||||||
|
*/
|
||||||
|
virtual std::string_view GetPassword() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the password provider.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationDefaultPasswordProvider : public NetworkAuthenticationPasswordProvider {
|
||||||
|
private:
|
||||||
|
const std::string *password; ///< The password to check against.
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the provider with the pointer to the password that is to be used. A pointer, so this can handle
|
||||||
|
* situations where the password gets changed over time.
|
||||||
|
* @param password The reference to the configured password.
|
||||||
|
*/
|
||||||
|
NetworkAuthenticationDefaultPasswordProvider(const std::string &password) : password(&password) {}
|
||||||
|
|
||||||
|
std::string_view GetPassword() const override { return *this->password; };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for server implementations to provide the authorized key validation.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationAuthorizedKeyHandler {
|
||||||
|
public:
|
||||||
|
virtual ~NetworkAuthenticationAuthorizedKeyHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the key handler can be used, i.e. whether there are authorized keys to check against.
|
||||||
|
* @return \c true when it can be used, otherwise \c false.
|
||||||
|
*/
|
||||||
|
virtual bool CanBeUsed() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given public key of the peer is allowed in.
|
||||||
|
* @param peer_public_key The public key of the peer to check against.
|
||||||
|
* @return \c true when the key is allowed, otherwise \c false.
|
||||||
|
*/
|
||||||
|
virtual bool IsAllowed(std::string_view peer_public_key) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation for the authorized key handler.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationDefaultAuthorizedKeyHandler : public NetworkAuthenticationAuthorizedKeyHandler {
|
||||||
|
private:
|
||||||
|
const std::vector<std::string> *authorized_keys; ///< The authorized keys to check against.
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler that uses the given authorized keys to check against.
|
||||||
|
* @param authorized_keys The reference to the authorized keys to check against.
|
||||||
|
*/
|
||||||
|
NetworkAuthenticationDefaultAuthorizedKeyHandler(const std::vector<std::string> &authorized_keys) : authorized_keys(&authorized_keys) {}
|
||||||
|
|
||||||
|
bool CanBeUsed() const override { return !this->authorized_keys->empty(); }
|
||||||
|
bool IsAllowed(std::string_view peer_public_key) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** The authentication method that can be used. */
|
||||||
|
enum NetworkAuthenticationMethod : uint8_t {
|
||||||
|
NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY, ///< No actual authentication is taking place, just perform a x25519 key exchange.
|
||||||
|
NETWORK_AUTH_METHOD_X25519_PAKE, ///< Authentication using x25519 password-authenticated key agreement.
|
||||||
|
NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY, ///< Authentication using x22519 key exchange and authorized keys.
|
||||||
|
NETWORK_AUTH_METHOD_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The mask of authentication methods that can be used. */
|
||||||
|
using NetworkAuthenticationMethodMask = uint16_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for cryptographic authentication handlers.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationHandler {
|
||||||
|
public:
|
||||||
|
virtual ~NetworkAuthenticationHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the handler for debug messages.
|
||||||
|
* @return The name of the handler.
|
||||||
|
*/
|
||||||
|
virtual std::string_view GetName() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the method this handler is providing functionality for.
|
||||||
|
* @return The \c NetworkAuthenticationMethod.
|
||||||
|
*/
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a \a NetworkEncryptionHandler to encrypt or decrypt messages from the client to the server.
|
||||||
|
* @return The handler for the client to server encryption.
|
||||||
|
*/
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a \a NetworkEncryptionHandler to encrypt or decrypt messages from the server to the client.
|
||||||
|
* @return The handler for the server to client encryption.
|
||||||
|
*/
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for client side cryptographic authentication handlers.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationClientHandler : public NetworkAuthenticationHandler {
|
||||||
|
public:
|
||||||
|
/** The processing result of receiving a request. */
|
||||||
|
enum RequestResult {
|
||||||
|
AWAIT_USER_INPUT, ///< We have requested some user input, but must wait on that.
|
||||||
|
READY_FOR_RESPONSE, ///< We do not have to wait for user input, and can immediately respond to the server.
|
||||||
|
INVALID, ///< We have received an invalid request.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a request from the server.
|
||||||
|
* @param p The packet to read the request from.
|
||||||
|
* @return True when valid, otherwise false.
|
||||||
|
*/
|
||||||
|
virtual RequestResult ReceiveRequest(struct Packet &p) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the response to send to the server.
|
||||||
|
* @param p The packet to write the response from.
|
||||||
|
* @return True when a valid packet was made, otherwise false.
|
||||||
|
*/
|
||||||
|
virtual bool SendResponse(struct Packet &p) = 0;
|
||||||
|
|
||||||
|
static std::unique_ptr<NetworkAuthenticationClientHandler> Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for server side cryptographic authentication handlers.
|
||||||
|
*/
|
||||||
|
class NetworkAuthenticationServerHandler : public NetworkAuthenticationHandler {
|
||||||
|
public:
|
||||||
|
/** The processing result of receiving a response. */
|
||||||
|
enum ResponseResult {
|
||||||
|
AUTHENTICATED, ///< The client was authenticated successfully.
|
||||||
|
NOT_AUTHENTICATED, ///< All authentications for this handler have been exhausted.
|
||||||
|
RETRY_NEXT_METHOD, ///< The client failed to authenticate, but there is another method to try.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the request to send to the client.
|
||||||
|
* @param p The packet to write the request to.
|
||||||
|
*/
|
||||||
|
virtual void SendRequest(struct Packet &p) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the response from the client.
|
||||||
|
* @param p The packet to read the response from.
|
||||||
|
* @return The \c ResponseResult describing the result.
|
||||||
|
*/
|
||||||
|
virtual ResponseResult ReceiveResponse(struct Packet &p) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this handler can be used with the current configuration.
|
||||||
|
* For example when there is no password, the handler cannot be used.
|
||||||
|
* @return True when this handler can be used.
|
||||||
|
*/
|
||||||
|
virtual bool CanBeUsed() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the public key the peer provided during the authentication.
|
||||||
|
* @return The hexadecimal string representation of the peer's public key.
|
||||||
|
*/
|
||||||
|
virtual std::string GetPeerPublicKey() const = 0;
|
||||||
|
|
||||||
|
static std::unique_ptr<NetworkAuthenticationServerHandler> Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask = ~static_cast<NetworkAuthenticationMethodMask>(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* NETWORK_CRYPTO_H */
|
341
src/network/network_crypto_internal.h
Normal file
341
src/network/network_crypto_internal.h
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file network_crypto_internal.h Internal bits to the crypto of the network handling. */
|
||||||
|
|
||||||
|
#ifndef NETWORK_CRYPTO_INTERNAL_H
|
||||||
|
#define NETWORK_CRYPTO_INTERNAL_H
|
||||||
|
|
||||||
|
#include "network_crypto.h"
|
||||||
|
|
||||||
|
/** The number of bytes the public and secret keys are in X25519. */
|
||||||
|
constexpr size_t X25519_KEY_SIZE = 32;
|
||||||
|
/** The number of bytes the nonces are in X25519. */
|
||||||
|
constexpr size_t X25519_NONCE_SIZE = 24;
|
||||||
|
/** The number of bytes the message authentication codes are in X25519. */
|
||||||
|
constexpr size_t X25519_MAC_SIZE = 16;
|
||||||
|
/** The number of bytes the (random) payload of the authentication message has. */
|
||||||
|
constexpr size_t X25519_KEY_EXCHANGE_MESSAGE_SIZE = 8;
|
||||||
|
|
||||||
|
/** Container for a X25519 key that is automatically crypto-wiped when destructed. */
|
||||||
|
struct X25519Key : std::array<uint8_t, X25519_KEY_SIZE> {
|
||||||
|
~X25519Key();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Container for a X25519 public key. */
|
||||||
|
struct X25519PublicKey : X25519Key {
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Container for a X25519 secret key. */
|
||||||
|
struct X25519SecretKey : X25519Key {
|
||||||
|
static X25519SecretKey CreateRandom();
|
||||||
|
X25519PublicKey CreatePublicKey() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Container for a X25519 nonce that is automatically crypto-wiped when destructed. */
|
||||||
|
struct X25519Nonce : std::array<uint8_t, X25519_NONCE_SIZE> {
|
||||||
|
static X25519Nonce CreateRandom();
|
||||||
|
~X25519Nonce();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Container for a X25519 message authentication code. */
|
||||||
|
using X25519Mac = std::array<uint8_t, X25519_MAC_SIZE>;
|
||||||
|
|
||||||
|
/** Container for a X25519 key exchange message. */
|
||||||
|
using X25519KeyExchangeMessage = std::array<uint8_t, X25519_KEY_EXCHANGE_MESSAGE_SIZE>;
|
||||||
|
|
||||||
|
/** The side of the key exchange. */
|
||||||
|
enum class X25519KeyExchangeSide {
|
||||||
|
CLIENT, ///< We are the client.
|
||||||
|
SERVER, ///< We are the server.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for the keys that derived from the X25519 key exchange mechanism. This mechanism derives
|
||||||
|
* a key to encrypt both the client-to-server and a key to encrypt server-to-client communication.
|
||||||
|
*/
|
||||||
|
class X25519DerivedKeys {
|
||||||
|
private:
|
||||||
|
/** Single contiguous buffer to store the derived keys in, as they are generated as a single hash. */
|
||||||
|
std::array<uint8_t, X25519_KEY_SIZE + X25519_KEY_SIZE> keys;
|
||||||
|
public:
|
||||||
|
~X25519DerivedKeys();
|
||||||
|
std::span<const uint8_t> ClientToServer() const;
|
||||||
|
std::span<const uint8_t> ServerToClient() const;
|
||||||
|
bool Exchange(const X25519PublicKey &peer_public_key, X25519KeyExchangeSide side,
|
||||||
|
const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base for handlers using a X25519 key exchange to perform authentication.
|
||||||
|
*
|
||||||
|
* In general this works as follows:
|
||||||
|
* 1) the client and server have or generate a secret and public X25519 key.
|
||||||
|
* 2) the X25519 key exchange is performed at both the client and server, with their own secret key and their peer's public key.
|
||||||
|
* 3) a pair of derived keys is created by BLAKE2b-hashing the following into 64 bytes, in this particular order:
|
||||||
|
* - the shared secret from the key exchange;
|
||||||
|
* - the public key of the server;
|
||||||
|
* - the public key of the client;
|
||||||
|
* - optional extra payload, e.g. a password in the case of PAKE.
|
||||||
|
* The first of the pair of derived keys is usually used to encrypt client-to-server communication, and the second of the pair
|
||||||
|
* is usually used to encrypt server-to-client communication.
|
||||||
|
* 4) a XChaCha20-Poly1305 (authenticated) encryption is performed using:
|
||||||
|
* - the first of the pair of derived keys as encryption key;
|
||||||
|
* - a 24 byte nonce;
|
||||||
|
* - the public key of the client as additional authenticated data.
|
||||||
|
* - a 8 byte random number as content/message.
|
||||||
|
*
|
||||||
|
* The server initiates the request by sending its public key and a 24 byte nonce that is randomly generated. Normally the side
|
||||||
|
* that sends the encrypted data sends the nonce in their packet, which would be the client on our case. However, there are
|
||||||
|
* many implementations of clients due to the admin-protocol where this is used, and we cannot guarantee that they generate a
|
||||||
|
* good enough nonce. As such the server sends one instead. The server will create a new set of keys for each session.
|
||||||
|
*
|
||||||
|
* The client receives the request, performs the key exchange, generates the derived keys and then encrypts the message. This
|
||||||
|
* message must contain some content, so it has to be filled with 8 random bytes. Once the message has been encrypted, the
|
||||||
|
* client sends their public key, the encrypted message and the message authentication code (MAC) to the server in a response.
|
||||||
|
*
|
||||||
|
* The server receives the response, performs the key exchange, generates the derived keys, decrypts the message and validates the
|
||||||
|
* message authentication code, and finally the message. It is up to the sub class to perform the final authentication checks.
|
||||||
|
*/
|
||||||
|
class X25519AuthenticationHandler {
|
||||||
|
private:
|
||||||
|
X25519SecretKey our_secret_key; ///< The secret key used by us.
|
||||||
|
X25519PublicKey our_public_key; ///< The public key used by us.
|
||||||
|
X25519Nonce nonce; ///< The nonce to prevent replay attacks.
|
||||||
|
X25519DerivedKeys derived_keys; ///< Keys derived from the authentication process.
|
||||||
|
X25519PublicKey peer_public_key; ///< The public key used by our peer.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
X25519AuthenticationHandler(const X25519SecretKey &secret_key);
|
||||||
|
|
||||||
|
void SendRequest(struct Packet &p);
|
||||||
|
bool ReceiveRequest(struct Packet &p);
|
||||||
|
bool SendResponse(struct Packet &p, std::string_view derived_key_extra_payload);
|
||||||
|
NetworkAuthenticationServerHandler::ResponseResult ReceiveResponse(struct Packet &p, std::string_view derived_key_extra_payload);
|
||||||
|
|
||||||
|
std::string GetPeerPublicKey() const;
|
||||||
|
|
||||||
|
std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const;
|
||||||
|
std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client side handler for using X25519 without actual authentication.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler, without an extra payload.
|
||||||
|
*/
|
||||||
|
class X25519KeyExchangeOnlyClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler that that one does the key exchange.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
*/
|
||||||
|
X25519KeyExchangeOnlyClientHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
|
||||||
|
|
||||||
|
virtual RequestResult ReceiveRequest(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveRequest(p) ? READY_FOR_RESPONSE : INVALID; }
|
||||||
|
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, {}); }
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-KeyExchangeOnly-client"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY; }
|
||||||
|
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server side handler for using X25519 without actual authentication.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler, without an extra payload.
|
||||||
|
*/
|
||||||
|
class X25519KeyExchangeOnlyServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler that that one does the key exchange.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
*/
|
||||||
|
X25519KeyExchangeOnlyServerHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
|
||||||
|
|
||||||
|
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
|
||||||
|
virtual ResponseResult ReceiveResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveResponse(p, {}); }
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-KeyExchangeOnly-server"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY; }
|
||||||
|
virtual bool CanBeUsed() const override { return true; }
|
||||||
|
|
||||||
|
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client side handler for using X25519 with a password-authenticated key exchange.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler, were the password is the extra payload.
|
||||||
|
*/
|
||||||
|
class X25519PAKEClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
|
||||||
|
private:
|
||||||
|
std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> handler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler with the given password handler.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
* @param handler The handler requesting the password from the user, if required.
|
||||||
|
*/
|
||||||
|
X25519PAKEClientHandler(const X25519SecretKey &secret_key, std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> handler) : X25519AuthenticationHandler(secret_key), handler(handler) {}
|
||||||
|
|
||||||
|
virtual RequestResult ReceiveRequest(struct Packet &p) override;
|
||||||
|
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, this->handler->password); }
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-PAKE-client"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_PAKE; }
|
||||||
|
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server side handler for using X25519 with a password-authenticated key exchange.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler, were the password is the extra payload.
|
||||||
|
*/
|
||||||
|
class X25519PAKEServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
|
||||||
|
private:
|
||||||
|
const NetworkAuthenticationPasswordProvider *password_provider; ///< The password to check against.
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler with the given password provider.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
* @param password_provider The provider for the passwords.
|
||||||
|
*/
|
||||||
|
X25519PAKEServerHandler(const X25519SecretKey &secret_key, const NetworkAuthenticationPasswordProvider *password_provider) : X25519AuthenticationHandler(secret_key), password_provider(password_provider) {}
|
||||||
|
|
||||||
|
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
|
||||||
|
virtual ResponseResult ReceiveResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveResponse(p, this->password_provider->GetPassword()); }
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-PAKE-server"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_PAKE; }
|
||||||
|
virtual bool CanBeUsed() const override { return !this->password_provider->GetPassword().empty(); }
|
||||||
|
|
||||||
|
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for clients using a X25519 key exchange to perform authentication via a set of authorized (public) keys of clients.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler. Once all these checks have succeeded, it will
|
||||||
|
* check whether the public key of the client is in the list of authorized keys to login.
|
||||||
|
*/
|
||||||
|
class X25519AuthorizedKeyClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler that uses the given password to check against.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
*/
|
||||||
|
X25519AuthorizedKeyClientHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
|
||||||
|
|
||||||
|
virtual RequestResult ReceiveRequest(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveRequest(p) ? READY_FOR_RESPONSE : INVALID; }
|
||||||
|
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, {}); }
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-AuthorizedKey-client"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY; }
|
||||||
|
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
|
||||||
|
static X25519SecretKey GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for servers using a X25519 key exchange to perform authentication via a set of authorized (public) keys of clients.
|
||||||
|
*
|
||||||
|
* This follows the method described in \c X25519AuthenticationHandler. Once all these checks have succeeded, it will
|
||||||
|
* check whether the public key of the client is in the list of authorized keys to login.
|
||||||
|
*/
|
||||||
|
class X25519AuthorizedKeyServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
|
||||||
|
private:
|
||||||
|
const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler; ///< The handler of the authorized keys.
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create the handler that uses the given authorized keys to check against.
|
||||||
|
* @param secret_key The secret key to initialize this handler with.
|
||||||
|
* @param authorized_key_handler The handler of the authorized keys.
|
||||||
|
*/
|
||||||
|
X25519AuthorizedKeyServerHandler(const X25519SecretKey &secret_key, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler) : X25519AuthenticationHandler(secret_key), authorized_key_handler(authorized_key_handler) {}
|
||||||
|
|
||||||
|
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
|
||||||
|
virtual ResponseResult ReceiveResponse(struct Packet &p) override;
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override { return "X25519-AuthorizedKey-server"; }
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY; }
|
||||||
|
virtual bool CanBeUsed() const override { return this->authorized_key_handler->CanBeUsed(); }
|
||||||
|
|
||||||
|
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for combining a number of authentication handlers, where the failure of one of the handlers will retry with
|
||||||
|
* another handler. For example when authorized keys fail, it can still fall back to a password.
|
||||||
|
*/
|
||||||
|
class CombinedAuthenticationClientHandler : public NetworkAuthenticationClientHandler {
|
||||||
|
public:
|
||||||
|
using Handler = std::unique_ptr<NetworkAuthenticationClientHandler>; ///< The type of the inner handlers.
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Handler> handlers; ///< The handlers that we can authenticate with.
|
||||||
|
NetworkAuthenticationClientHandler *current_handler = nullptr; ///< The currently active handler.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Add the given sub-handler to this handler.
|
||||||
|
* @param handler The handler to add.
|
||||||
|
*/
|
||||||
|
void Add(Handler &&handler) { this->handlers.push_back(std::move(handler)); }
|
||||||
|
|
||||||
|
virtual RequestResult ReceiveRequest(struct Packet &p) override;
|
||||||
|
virtual bool SendResponse(struct Packet &p) override;
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override;
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->current_handler->CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->current_handler->CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for combining a number of authentication handlers, where the failure of one of the handlers will retry with
|
||||||
|
* another handler. For example when authorized keys fail, it can still fall back to a password.
|
||||||
|
*/
|
||||||
|
class CombinedAuthenticationServerHandler : public NetworkAuthenticationServerHandler {
|
||||||
|
public:
|
||||||
|
using Handler = std::unique_ptr<NetworkAuthenticationServerHandler>; ///< The type of the inner handlers.
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Handler> handlers; ///< The handlers that we can (still) authenticate with.
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Add(Handler &&handler);
|
||||||
|
|
||||||
|
virtual void SendRequest(struct Packet &p) override;
|
||||||
|
virtual ResponseResult ReceiveResponse(struct Packet &p) override;
|
||||||
|
|
||||||
|
virtual std::string_view GetName() const override;
|
||||||
|
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override;
|
||||||
|
virtual bool CanBeUsed() const override;
|
||||||
|
|
||||||
|
virtual std::string GetPeerPublicKey() const override { return this->handlers.back()->GetPeerPublicKey(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->handlers.back()->CreateClientToServerEncryptionHandler(); }
|
||||||
|
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->handlers.back()->CreateServerToClientEncryptionHandler(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* NETWORK_CRYPTO_INTERNAL_H */
|
@ -9,6 +9,7 @@ add_test_files(
|
|||||||
string_func.cpp
|
string_func.cpp
|
||||||
strings_func.cpp
|
strings_func.cpp
|
||||||
test_main.cpp
|
test_main.cpp
|
||||||
|
test_network_crypto.cpp
|
||||||
test_script_admin.cpp
|
test_script_admin.cpp
|
||||||
test_window_desc.cpp
|
test_window_desc.cpp
|
||||||
)
|
)
|
||||||
|
199
src/tests/test_network_crypto.cpp
Normal file
199
src/tests/test_network_crypto.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file test_network_crypto.cpp Tests for network related crypto functions. */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
|
||||||
|
#include "../3rdparty/catch2/catch.hpp"
|
||||||
|
|
||||||
|
#include "../core/format.hpp"
|
||||||
|
#include "../network/network_crypto_internal.h"
|
||||||
|
#include "../network/core/packet.h"
|
||||||
|
#include "../string_func.h"
|
||||||
|
|
||||||
|
class MockNetworkSocketHandler : public NetworkSocketHandler {
|
||||||
|
};
|
||||||
|
|
||||||
|
static MockNetworkSocketHandler mock_socket_handler;
|
||||||
|
|
||||||
|
static Packet CreatePacketForReading(Packet &source)
|
||||||
|
{
|
||||||
|
source.PrepareToSend();
|
||||||
|
|
||||||
|
Packet dest(&mock_socket_handler, COMPAT_MTU, source.Size());
|
||||||
|
|
||||||
|
auto transfer_in = [](Packet &source, char *dest_data, size_t length) {
|
||||||
|
auto transfer_out = [](char *dest_data, const char *source_data, size_t length) {
|
||||||
|
std::copy(source_data, source_data + length, dest_data);
|
||||||
|
return length;
|
||||||
|
};
|
||||||
|
return source.TransferOutWithLimit(transfer_out, length, dest_data);
|
||||||
|
};
|
||||||
|
dest.TransferIn(transfer_in, source);
|
||||||
|
|
||||||
|
dest.PrepareToRead();
|
||||||
|
dest.Recv_uint8(); // Ignore the type
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestPasswordRequestHandler : public NetworkAuthenticationPasswordRequestHandler {
|
||||||
|
private:
|
||||||
|
std::string password;
|
||||||
|
public:
|
||||||
|
TestPasswordRequestHandler(std::string &password) : password(password) {}
|
||||||
|
void SendResponse() override {}
|
||||||
|
void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) override { request->Reply(this->password); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void TestAuthentication(NetworkAuthenticationServerHandler &server, NetworkAuthenticationClientHandler &client,
|
||||||
|
NetworkAuthenticationServerHandler::ResponseResult expected_response_result,
|
||||||
|
NetworkAuthenticationClientHandler::RequestResult expected_request_result)
|
||||||
|
{
|
||||||
|
Packet request(&mock_socket_handler, PacketType{});
|
||||||
|
server.SendRequest(request);
|
||||||
|
|
||||||
|
request = CreatePacketForReading(request);
|
||||||
|
CHECK(client.ReceiveRequest(request) == expected_request_result);
|
||||||
|
|
||||||
|
Packet response(&mock_socket_handler, PacketType{});
|
||||||
|
client.SendResponse(response);
|
||||||
|
|
||||||
|
response = CreatePacketForReading(response);
|
||||||
|
CHECK(server.ReceiveResponse(response) == expected_response_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Authentication_KeyExchangeOnly")
|
||||||
|
{
|
||||||
|
X25519KeyExchangeOnlyServerHandler server(X25519SecretKey::CreateRandom());
|
||||||
|
X25519KeyExchangeOnlyClientHandler client(X25519SecretKey::CreateRandom());
|
||||||
|
|
||||||
|
TestAuthentication(server, client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void TestAuthenticationPAKE(std::string server_password, std::string client_password,
|
||||||
|
NetworkAuthenticationServerHandler::ResponseResult expected_response_result)
|
||||||
|
{
|
||||||
|
NetworkAuthenticationDefaultPasswordProvider server_password_provider(server_password);
|
||||||
|
X25519PAKEServerHandler server(X25519SecretKey::CreateRandom(), &server_password_provider);
|
||||||
|
X25519PAKEClientHandler client(X25519SecretKey::CreateRandom(), std::make_shared<TestPasswordRequestHandler>(client_password));
|
||||||
|
|
||||||
|
TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Authentication_PAKE")
|
||||||
|
{
|
||||||
|
SECTION("Correct password") {
|
||||||
|
TestAuthenticationPAKE("sikrit", "sikrit", NetworkAuthenticationServerHandler::AUTHENTICATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Empty password") {
|
||||||
|
TestAuthenticationPAKE("", "", NetworkAuthenticationServerHandler::AUTHENTICATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Wrong password") {
|
||||||
|
TestAuthenticationPAKE("sikrit", "secret", NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void TestAuthenticationAuthorizedKey(const X25519SecretKey &client_secret_key, const X25519PublicKey &server_expected_public_key,
|
||||||
|
NetworkAuthenticationServerHandler::ResponseResult expected_response_result)
|
||||||
|
{
|
||||||
|
std::vector<std::string> authorized_keys;
|
||||||
|
authorized_keys.emplace_back(FormatArrayAsHex(server_expected_public_key));
|
||||||
|
|
||||||
|
NetworkAuthenticationDefaultAuthorizedKeyHandler authorized_key_handler(authorized_keys);
|
||||||
|
X25519AuthorizedKeyServerHandler server(X25519SecretKey::CreateRandom(), &authorized_key_handler);
|
||||||
|
X25519AuthorizedKeyClientHandler client(client_secret_key);
|
||||||
|
|
||||||
|
TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Authentication_AuthorizedKey")
|
||||||
|
{
|
||||||
|
auto client_secret_key = X25519SecretKey::CreateRandom();
|
||||||
|
auto valid_client_public_key = client_secret_key.CreatePublicKey();
|
||||||
|
auto invalid_client_public_key = X25519SecretKey::CreateRandom().CreatePublicKey();
|
||||||
|
|
||||||
|
SECTION("Correct public key") {
|
||||||
|
TestAuthenticationAuthorizedKey(client_secret_key, valid_client_public_key, NetworkAuthenticationServerHandler::AUTHENTICATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Incorrect public key") {
|
||||||
|
TestAuthenticationAuthorizedKey(client_secret_key, invalid_client_public_key, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Authentication_Combined")
|
||||||
|
{
|
||||||
|
auto client_secret_key = X25519SecretKey::CreateRandom();
|
||||||
|
std::string client_secret_key_str = FormatArrayAsHex(client_secret_key);
|
||||||
|
auto client_public_key = client_secret_key.CreatePublicKey();
|
||||||
|
std::string client_public_key_str = FormatArrayAsHex(client_public_key);
|
||||||
|
|
||||||
|
std::vector<std::string> valid_authorized_keys;
|
||||||
|
valid_authorized_keys.emplace_back(client_public_key_str);
|
||||||
|
NetworkAuthenticationDefaultAuthorizedKeyHandler valid_authorized_key_handler(valid_authorized_keys);
|
||||||
|
|
||||||
|
std::vector<std::string> invalid_authorized_keys;
|
||||||
|
invalid_authorized_keys.emplace_back("not-a-valid-authorized-key");
|
||||||
|
NetworkAuthenticationDefaultAuthorizedKeyHandler invalid_authorized_key_handler(invalid_authorized_keys);
|
||||||
|
|
||||||
|
std::vector<std::string> no_authorized_keys;
|
||||||
|
NetworkAuthenticationDefaultAuthorizedKeyHandler no_authorized_key_handler(no_authorized_keys);
|
||||||
|
|
||||||
|
std::string no_password = "";
|
||||||
|
NetworkAuthenticationDefaultPasswordProvider no_password_provider(no_password);
|
||||||
|
std::string valid_password = "sikrit";
|
||||||
|
NetworkAuthenticationDefaultPasswordProvider valid_password_provider(valid_password);
|
||||||
|
std::string invalid_password = "secret";
|
||||||
|
NetworkAuthenticationDefaultPasswordProvider invalid_password_provider(invalid_password);
|
||||||
|
|
||||||
|
auto client = NetworkAuthenticationClientHandler::Create(std::make_shared<TestPasswordRequestHandler>(valid_password), client_secret_key_str, client_public_key_str);
|
||||||
|
|
||||||
|
SECTION("Invalid authorized keys, invalid password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &invalid_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Invalid authorized keys, valid password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &invalid_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Valid authorized keys, valid password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &valid_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("No authorized keys, invalid password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &no_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("No authorized keys, valid password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &no_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("No authorized keys, no password") {
|
||||||
|
auto server = NetworkAuthenticationServerHandler::Create(&no_password_provider, &no_authorized_key_handler);
|
||||||
|
|
||||||
|
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user