/* * Copyright (c) 2014, Oculus VR, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ /// \file TwoWayAuthentication.h /// \brief Implements two way authentication /// \details Given two systems, each of whom known a common password, verify the password without transmitting it /// This can be used to determine what permissions are should be allowed to the other system /// #include "NativeFeatureIncludes.hpp" #if _RAKNET_SUPPORT_TwoWayAuthentication==1 #ifndef __TWO_WAY_AUTHENTICATION_H #define __TWO_WAY_AUTHENTICATION_H // How often to change the nonce. #define NONCE_TIMEOUT_MS 10000 // How often to check for ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, and the minimum timeout time. Maximum is double this value. #define CHALLENGE_MINIMUM_TIMEOUT 3000 #if LIBCAT_SECURITY==1 // From CPP FILE: // static const int HASH_BITS = 256; // static const int HASH_BYTES = HASH_BITS / 8; // static const int STRENGTHENING_FACTOR = 1000; #define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 32 #define HASHED_NONCE_AND_PW_LENGTH 32 #else #include "DR_SHA1.hpp" #define TWO_WAY_AUTHENTICATION_NONCE_LENGTH 20 #define HASHED_NONCE_AND_PW_LENGTH SHA1_LENGTH #endif #include "PluginInterface2.hpp" #include "RakMemoryOverride.hpp" #include "NativeTypes.hpp" #include "RakString.hpp" #include "DS_Hash.hpp" #include "DS_Queue.hpp" typedef int64_t FCM2Guid; namespace RakNet { /// Forward declarations class RakPeerInterface; /// \brief Implements two way authentication /// \details Given two systems, each of whom known a common password / identifier pair, verify the password without transmitting it /// This can be used to determine what permissions are should be allowed to the other system /// If the other system should not send any data until authentication passes, you can use the MessageFilter plugin for this. Call MessageFilter::SetAllowMessageID() including ID_TWO_WAY_AUTHENTICATION_NEGOTIATION when doing so. Also attach MessageFilter first in the list of plugins /// \note If other systems challenges us, and fails, you will get ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILED. /// \ingroup PLUGINS_GROUP class RAK_DLL_EXPORT TwoWayAuthentication : public PluginInterface2 { public: // GetInstance() and DestroyInstance(instance*) STATIC_FACTORY_DECLARATIONS(TwoWayAuthentication) TwoWayAuthentication(); virtual ~TwoWayAuthentication(); /// \brief Adds a password to the list of passwords the system will accept /// \details Each password, which is secret and not transmitted, is identified by \a identifier. /// \a identifier is transmitted in plaintext with the request. It is only needed because the system supports multiple password. /// It is used to only hash against once password on the remote system, rather than having to hash against every known password. /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure /// \param[in] password The password to add /// \return True on success, false on identifier==password, either identifier or password is blank, or identifier is already in use bool AddPassword(RakNet::RakString identifier, RakNet::RakString password); /// \brief Challenge another system for the specified identifier /// \details After calling Challenge, you will get back ID_TWO_WAY_AUTHENTICATION_SUCCESS, ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT, or ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILED /// ID_TWO_WAY_AUTHENTICATION_SUCCESS will be returned if and only if the other system has called AddPassword() with the same identifier\password pair as this system. /// \param[in] identifier A unique identifier representing this password. This is transmitted in plaintext and should be considered insecure /// \return True on success, false on remote system not connected, or identifier not previously added with AddPassword() bool Challenge(RakNet::RakString identifier, AddressOrGUID remoteSystem); /// \brief Free all memory void Clear(void); /// \internal virtual void Update(void); /// \internal virtual PluginReceiveResult OnReceive(Packet *packet); /// \internal virtual void OnRakPeerShutdown(void); /// \internal virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); /// \internal struct PendingChallenge { RakNet::RakString identifier; AddressOrGUID remoteSystem; RakNet::Time time; bool sentHash; }; DataStructures::Queue outgoingChallenges; /// \internal struct NonceAndRemoteSystemRequest { char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]; RakNet::AddressOrGUID remoteSystem; unsigned short requestId; RakNet::Time whenGenerated; }; /// \internal struct RAK_DLL_EXPORT NonceGenerator { NonceGenerator(); ~NonceGenerator(); void GetNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short *requestId, RakNet::AddressOrGUID remoteSystem); void GenerateNonce(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH]); bool GetNonceById(char nonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], unsigned short requestId, RakNet::AddressOrGUID remoteSystem, bool popIfFound); void Clear(void); void ClearByAddress(RakNet::AddressOrGUID remoteSystem); void Update(RakNet::Time curTime); DataStructures::List generatedNonces; unsigned short nextRequestId; }; protected: void PushToUser(MessageID messageId, RakNet::RakString password, RakNet::AddressOrGUID remoteSystem); // Key is identifier, data is password DataStructures::Hash passwords; RakNet::Time whenLastTimeoutCheck; NonceGenerator nonceGenerator; void OnNonceRequest(Packet *packet); void OnNonceReply(Packet *packet); PluginReceiveResult OnHashedNonceAndPassword(Packet *packet); void OnPasswordResult(Packet *packet); void Hash(char thierNonce[TWO_WAY_AUTHENTICATION_NONCE_LENGTH], RakNet::RakString password, char out[HASHED_NONCE_AND_PW_LENGTH]); }; } // namespace RakNet #endif #endif // _RAKNET_SUPPORT_*