diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client.cpp | 134 | ||||
-rw-r--r-- | src/client.h | 29 | ||||
-rw-r--r-- | src/client/clientlauncher.cpp | 2 | ||||
-rw-r--r-- | src/clientiface.cpp | 63 | ||||
-rw-r--r-- | src/clientiface.h | 230 | ||||
-rw-r--r-- | src/network/clientopcodes.cpp | 38 | ||||
-rw-r--r-- | src/network/clientpackethandler.cpp | 99 | ||||
-rw-r--r-- | src/network/networkpacket.h | 4 | ||||
-rw-r--r-- | src/network/networkprotocol.h | 120 | ||||
-rw-r--r-- | src/network/serveropcodes.cpp | 40 | ||||
-rw-r--r-- | src/network/serverpackethandler.cpp | 472 | ||||
-rw-r--r-- | src/script/lua_api/l_util.cpp | 1 | ||||
-rw-r--r-- | src/server.cpp | 38 | ||||
-rw-r--r-- | src/server.h | 6 | ||||
-rw-r--r-- | src/util/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/util/auth.cpp | 126 | ||||
-rw-r--r-- | src/util/auth.h | 37 | ||||
-rw-r--r-- | src/util/md32_common.h | 428 | ||||
-rw-r--r-- | src/util/sha2.h | 154 | ||||
-rw-r--r-- | src/util/sha256.c | 404 | ||||
-rw-r--r-- | src/util/srp.cpp | 1029 | ||||
-rw-r--r-- | src/util/srp.h | 171 | ||||
-rw-r--r-- | src/util/string.cpp | 23 | ||||
-rw-r--r-- | src/util/string.h | 3 |
24 files changed, 3346 insertions, 308 deletions
diff --git a/src/client.cpp b/src/client.cpp index ceea56ba0..8cfcc85a7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -22,10 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <sstream> #include <IFileSystem.h> #include "jthread/jmutexautolock.h" +#include "util/auth.h" #include "util/directiontables.h" #include "util/pointedthing.h" #include "util/serialize.h" #include "util/string.h" +#include "util/srp.h" #include "client.h" #include "network/clientopcodes.h" #include "filesys.h" @@ -255,6 +257,8 @@ Client::Client( m_highlighted_pos(0,0,0), m_map_seed(0), m_password(password), + m_chosen_auth_mech(AUTH_MECHANISM_NONE), + m_auth_data(NULL), m_access_denied(false), m_itemdef_received(false), m_nodedef_received(false), @@ -404,10 +408,13 @@ void Client::step(float dtime) memset(pName, 0, PLAYERNAME_SIZE * sizeof(char)); memset(pPassword, 0, PASSWORD_SIZE * sizeof(char)); + std::string hashed_password = translatePassword(myplayer->getName(), m_password); snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName()); - snprintf(pPassword, PASSWORD_SIZE, "%s", m_password.c_str()); + snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str()); sendLegacyInit(pName, pPassword); + if (LATEST_PROTOCOL_VERSION >= 25) + sendInit(myplayer->getName()); } // Not connected, return @@ -943,6 +950,39 @@ void Client::interact(u8 action, const PointedThing& pointed) Send(&pkt); } +void Client::deleteAuthData() +{ + if (!m_auth_data) + return; + + switch (m_chosen_auth_mech) { + case AUTH_MECHANISM_FIRST_SRP: + break; + case AUTH_MECHANISM_SRP: + case AUTH_MECHANISM_LEGACY_PASSWORD: + srp_user_delete((SRPUser *) m_auth_data); + m_auth_data = NULL; + break; + case AUTH_MECHANISM_NONE: + break; + } +} + + +AuthMechanism Client::choseAuthMech(const u32 mechs) +{ + if (mechs & AUTH_MECHANISM_SRP) + return AUTH_MECHANISM_SRP; + + if (mechs & AUTH_MECHANISM_FIRST_SRP) + return AUTH_MECHANISM_FIRST_SRP; + + if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD) + return AUTH_MECHANISM_LEGACY_PASSWORD; + + return AUTH_MECHANISM_NONE; +} + void Client::sendLegacyInit(const char* playerName, const char* playerPassword) { NetworkPacket pkt(TOSERVER_INIT_LEGACY, @@ -956,6 +996,70 @@ void Client::sendLegacyInit(const char* playerName, const char* playerPassword) Send(&pkt); } +void Client::sendInit(const std::string &playerName) +{ + NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); + + // TODO (later) actually send supported compression modes + pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u8) 42; + pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX; + pkt << playerName; + + Send(&pkt); +} + +void Client::startAuth(AuthMechanism chosen_auth_mechanism) +{ + m_chosen_auth_mech = chosen_auth_mechanism; + + switch (chosen_auth_mechanism) { + case AUTH_MECHANISM_FIRST_SRP: { + // send srp verifier to server + NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0); + char *salt, *bytes_v; + std::size_t len_salt, len_v; + salt = NULL; + getSRPVerifier(getPlayerName(), m_password, + &salt, &len_salt, &bytes_v, &len_v); + resp_pkt + << std::string((char*)salt, len_salt) + << std::string((char*)bytes_v, len_v) + << (u8)((m_password == "") ? 1 : 0); + free(salt); + free(bytes_v); + Send(&resp_pkt); + break; + } + case AUTH_MECHANISM_SRP: + case AUTH_MECHANISM_LEGACY_PASSWORD: { + u8 based_on = 1; + + if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) { + m_password = translatePassword(getPlayerName(), m_password); + based_on = 0; + } + + std::string playername_u = lowercase(getPlayerName()); + m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048, + getPlayerName().c_str(), playername_u.c_str(), + (const unsigned char *) m_password.c_str(), + m_password.length(), NULL, NULL); + char *bytes_A = 0; + size_t len_A = 0; + srp_user_start_authentication((struct SRPUser *) m_auth_data, + NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A); + + NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0); + resp_pkt << std::string(bytes_A, len_A) << based_on; + free(bytes_A); + Send(&resp_pkt); + break; + } + case AUTH_MECHANISM_NONE: + break; // not handled in this method + } +} + void Client::sendDeletedBlocks(std::vector<v3s16> &blocks) { NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size()); @@ -1066,24 +1170,30 @@ void Client::sendChangePassword(const std::string &oldpassword, const std::string &newpassword) { Player *player = m_env.getLocalPlayer(); - if(player == NULL) + if (player == NULL) return; std::string playername = player->getName(); - std::string oldpwd = translatePassword(playername, oldpassword); - std::string newpwd = translatePassword(playername, newpassword); + if (m_proto_ver >= 25) { + // get into sudo mode and then send new password to server + m_password = oldpassword; + m_new_password = newpassword; + startAuth(choseAuthMech(m_sudo_auth_methods)); + } else { + std::string oldpwd = translatePassword(playername, oldpassword); + std::string newpwd = translatePassword(playername, newpassword); - NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE); + NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE); - for(u8 i = 0; i < PASSWORD_SIZE; i++) { - pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0); - } + for (u8 i = 0; i < PASSWORD_SIZE; i++) { + pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0); + } - for(u8 i = 0; i < PASSWORD_SIZE; i++) { - pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0); + for (u8 i = 0; i < PASSWORD_SIZE; i++) { + pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0); + } + Send(&pkt); } - - Send(&pkt); } diff --git a/src/client.h b/src/client.h index 082b3d6dd..87cf8ce45 100644 --- a/src/client.h +++ b/src/client.h @@ -351,6 +351,8 @@ public: void handleCommand_Deprecated(NetworkPacket* pkt); void handleCommand_Hello(NetworkPacket* pkt); void handleCommand_AuthAccept(NetworkPacket* pkt); + void handleCommand_AcceptSudoMode(NetworkPacket* pkt); + void handleCommand_DenySudoMode(NetworkPacket* pkt); void handleCommand_InitLegacy(NetworkPacket* pkt); void handleCommand_AccessDenied(NetworkPacket* pkt); void handleCommand_RemoveNode(NetworkPacket* pkt); @@ -391,6 +393,7 @@ public: void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt); void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt); void handleCommand_EyeOffset(NetworkPacket* pkt); + void handleCommand_SrpBytesSandB(NetworkPacket* pkt); void ProcessData(NetworkPacket *pkt); @@ -542,11 +545,21 @@ private: // Send the item number 'item' as player item to the server void sendPlayerItem(u16 item); + void deleteAuthData(); + // helper method shared with clientpackethandler + static AuthMechanism choseAuthMech(const u32 mechs); + void sendLegacyInit(const char* playerName, const char* playerPassword); + void sendInit(const std::string &playerName); + void startAuth(AuthMechanism chosen_auth_mechanism); void sendDeletedBlocks(std::vector<v3s16> &blocks); void sendGotBlocks(v3s16 block); void sendRemovedSounds(std::vector<s32> &soundList); + // Helper function + inline std::string getPlayerName() + { return m_env.getLocalPlayer()->getName(); } + float m_packetcounter_timer; float m_connection_reinit_timer; float m_avg_rtt_timer; @@ -569,6 +582,8 @@ private: IrrlichtDevice *m_device; // Server serialization version u8 m_server_ser_ver; + // Used version of the protocol with server + u8 m_proto_ver; u16 m_playeritem; bool m_inventory_updated; Inventory *m_inventory_from_server; @@ -584,9 +599,23 @@ private: //s32 m_daynight_i; //u32 m_daynight_ratio; std::queue<std::wstring> m_chat_queue; + + // The authentication methods we can use to enter sudo mode (=change password) + u32 m_sudo_auth_methods; + // The seed returned by the server in TOCLIENT_INIT is stored here u64 m_map_seed; + + // Auth data + std::string m_playername; std::string m_password; + // If set, this will be sent (and cleared) upon a TOCLIENT_ACCEPT_SUDO_MODE + std::string m_new_password; + // Usable by auth mechanisms. + AuthMechanism m_chosen_auth_mech; + void * m_auth_data; + + bool m_access_denied; std::string m_access_denied_reason; std::queue<ClientEvent> m_client_event_queue; diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index d6327e259..41ba4f307 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -392,7 +392,7 @@ bool ClientLauncher::launch_game(std::string &error_message, else playername = menudata.name; - password = translatePassword(playername, menudata.password); + password = menudata.password; g_settings->set("name", playername); diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 7649be29e..6944e56db 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "emerge.h" #include "serverobject.h" // TODO this is used for cleanup of only #include "log.h" +#include "util/srp.h" const char *ClientInterface::statenames[] = { "Invalid", @@ -427,10 +428,12 @@ void RemoteClient::notifyEvent(ClientStateEvent event) //intentionally do nothing break; case CS_Created: - switch(event) - { - case CSE_Init: - m_state = CS_InitSent; + switch (event) { + case CSE_Hello: + m_state = CS_HelloSent; + break; + case CSE_InitLegacy: + m_state = CS_AwaitingInit2; break; case CSE_Disconnect: m_state = CS_Disconnecting; @@ -447,7 +450,32 @@ void RemoteClient::notifyEvent(ClientStateEvent event) case CS_Denied: /* don't do anything if in denied state */ break; - case CS_InitSent: + case CS_HelloSent: + switch(event) + { + case CSE_AuthAccept: + m_state = CS_AwaitingInit2; + if ((chosen_mech == AUTH_MECHANISM_SRP) + || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) + srp_verifier_delete((SRPVerifier *) auth_data); + chosen_mech = AUTH_MECHANISM_NONE; + break; + case CSE_Disconnect: + m_state = CS_Disconnecting; + break; + case CSE_SetDenied: + m_state = CS_Denied; + if ((chosen_mech == AUTH_MECHANISM_SRP) + || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) + srp_verifier_delete((SRPVerifier *) auth_data); + chosen_mech = AUTH_MECHANISM_NONE; + break; + default: + myerror << "HelloSent: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); + } + break; + case CS_AwaitingInit2: switch(event) { case CSE_GotInit2: @@ -514,6 +542,13 @@ void RemoteClient::notifyEvent(ClientStateEvent event) case CSE_Disconnect: m_state = CS_Disconnecting; break; + case CSE_SudoSuccess: + m_state = CS_SudoMode; + if ((chosen_mech == AUTH_MECHANISM_SRP) + || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) + srp_verifier_delete((SRPVerifier *) auth_data); + chosen_mech = AUTH_MECHANISM_NONE; + break; /* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */ default: myerror << "Active: Invalid client state transition! " << event; @@ -521,6 +556,24 @@ void RemoteClient::notifyEvent(ClientStateEvent event) break; } break; + case CS_SudoMode: + switch(event) + { + case CSE_SetDenied: + m_state = CS_Denied; + break; + case CSE_Disconnect: + m_state = CS_Disconnecting; + break; + case CSE_SudoLeave: + m_state = CS_Active; + break; + default: + myerror << "Active: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); + break; + } + break; case CS_Disconnecting: /* we are already disconnecting */ break; diff --git a/src/clientiface.h b/src/clientiface.h index 54b250265..070559c3a 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -47,96 +47,119 @@ class EmergeManager; | Created | | | \-----------------/ - | - | -+-----------------------------+ invalid playername, password -|IN: | or denied by mod -| TOSERVER_INIT |------------------------------ -+-----------------------------+ | - | | - | Auth ok | - | | -+-----------------------------+ | -|OUT: | | -| TOCLIENT_INIT | | -+-----------------------------+ | - | | - v | - /-----------------\ | - | | | - | InitSent | | - | | | - \-----------------/ +------------------ - | | | -+-----------------------------+ +-----------------------------+ | -|IN: | |OUT: | | -| TOSERVER_INIT2 | | TOCLIENT_ACCESS_DENIED | | -+-----------------------------+ +-----------------------------+ | - | | | - v v | - /-----------------\ /-----------------\ | - | | | | | - | InitDone | | Denied | | - | | | | | - \-----------------/ \-----------------/ | - | | -+-----------------------------+ | -|OUT: | | -| TOCLIENT_MOVEMENT | | -| TOCLIENT_ITEMDEF | | -| TOCLIENT_NODEDEF | | -| TOCLIENT_ANNOUNCE_MEDIA | | -| TOCLIENT_DETACHED_INVENTORY | | -| TOCLIENT_TIME_OF_DAY | | -+-----------------------------+ | - | | - | | - | ----------------------------------- | - v | | | - /-----------------\ v | - | | +-----------------------------+ | - | DefinitionsSent | |IN: | | - | | | TOSERVER_REQUEST_MEDIA | | - \-----------------/ | TOSERVER_RECEIVED_MEDIA | | - | +-----------------------------+ | - | ^ | | - | ----------------------------------- | - | | -+-----------------------------+ | -|IN: | | -| TOSERVER_CLIENT_READY | | -+-----------------------------+ | - | async | - v mod action | -+-----------------------------+ (ban,kick) | -|OUT: | | -| TOCLIENT_MOVE_PLAYER | | -| TOCLIENT_PRIVILEGES | | -| TOCLIENT_INVENTORY_FORMSPEC | | -| UpdateCrafting | | -| TOCLIENT_INVENTORY | | -| TOCLIENT_HP (opt) | | -| TOCLIENT_BREATH | | -| TOCLIENT_DEATHSCREEN | | -+-----------------------------+ | - | | - v | - /-----------------\ | - | |------------------------------------------------------ - | Active | - | |---------------------------------- - \-----------------/ timeout | - | +-----------------------------+ - | |OUT: | - | | TOCLIENT_DISCONNECT | - | +-----------------------------+ - | | - | v -+-----------------------------+ /-----------------\ -|IN: | | | -| TOSERVER_DISCONNECT |------------------->| Disconnecting | -+-----------------------------+ | | - \-----------------/ + | depending of the incoming packet + +--------------------------------------- + v | ++-----------------------------+ +-----------------------------+ +|IN: | |IN: | +| TOSERVER_INIT_LEGACY |----- | TOSERVER_INIT | invalid playername, ++-----------------------------+ | +-----------------------------+ password (for _LEGACY), + | | | or denied by mod + | Auth ok -------------------+--------------------------------- + | | | ++-----------------------------+ +-----------------------------+ | +|OUT: | |OUT: | | +| TOCLIENT_INIT_LEGACY | | TOCLIENT_HELLO | | ++-----------------------------+ +-----------------------------+ | + | | | + | | | + v v | + /-----------------\ /-----------------\ | + | | | | | + | AwaitingInit2 |<--------- | HelloSent | | + | | | | | | + \-----------------/ | \-----------------/ | + | | | | ++-----------------------------+ | *-----------------------------* Auth fails | +|IN: | | |Authentication, depending on |-----------------+ +| TOSERVER_INIT2 | | | packet sent by client | | ++-----------------------------+ | *-----------------------------* | + | | | | + | | | Authentication | + v | | successful | + /-----------------\ | v | + | | | +-----------------------------+ | + | InitDone | | |OUT: | | + | | | | TOCLIENT_AUTH_ACCEPT | | + \-----------------/ | +-----------------------------+ | + | | | | ++-----------------------------+ --------------------- | +|OUT: | | +| TOCLIENT_MOVEMENT | | +| TOCLIENT_ITEMDEF | | +| TOCLIENT_NODEDEF | | +| TOCLIENT_ANNOUNCE_MEDIA | | +| TOCLIENT_DETACHED_INVENTORY | | +| TOCLIENT_TIME_OF_DAY | | ++-----------------------------+ | + | | + | | + | ----------------------------- | + v | | | + /-----------------\ v | + | | +-----------------------------+ | + | DefinitionsSent | |IN: | | + | | | TOSERVER_REQUEST_MEDIA | | + \-----------------/ | TOSERVER_RECEIVED_MEDIA | | + | +-----------------------------+ | + | ^ | | + | ----------------------------- | + | | ++-----------------------------+ --------------------------------+ +|IN: | | | +| TOSERVER_CLIENT_READY | v | ++-----------------------------+ +-------------------------------+ | + | |OUT: | | + v | TOCLIENT_ACCESS_DENIED_LEGAGY | | ++-----------------------------+ +-------------------------------+ | +|OUT: | | | +| TOCLIENT_MOVE_PLAYER | v | +| TOCLIENT_PRIVILEGES | /-----------------\ | +| TOCLIENT_INVENTORY_FORMSPEC | | | | +| UpdateCrafting | | Denied | | +| TOCLIENT_INVENTORY | | | | +| TOCLIENT_HP (opt) | \-----------------/ | +| TOCLIENT_BREATH | | +| TOCLIENT_DEATHSCREEN | | ++-----------------------------+ | + | | + v | + /-----------------\ async mod action (ban, kick) | + | |--------------------------------------------------------------- + ---->| Active | + | | |---------------------------------------------- + | \-----------------/ timeout | + | | | +-----------------------------+ + | | | |OUT: | + | | | | TOCLIENT_DISCONNECT | + | | | +-----------------------------+ + | | | | + | | | v + | | +-----------------------------+ /-----------------\ + | | |IN: | | | + | | | TOSERVER_DISCONNECT |------------------->| Disconnecting | + | | +-----------------------------+ | | + | | \-----------------/ + | | any auth packet which was + | | allowed in TOCLIENT_AUTH_ACCEPT + | v + | *-----------------------------* Auth +-------------------------------+ + | |Authentication, depending on | succeeds |OUT: | + | | packet sent by client |---------->| TOCLIENT_ACCEPT_SUDO_MODE | + | *-----------------------------* +-------------------------------+ + | | | + | | Auth fails /-----------------\ + | v | | + | +-------------------------------+ | SudoMode | + | |OUT: | | | + | | TOCLIENT_DENY_SUDO_MODE | \-----------------/ + | +-------------------------------+ | + | | v + | | +-----------------------------+ + | | sets password accordingly |IN: | + -------------------+-------------------------------| TOSERVER_FIRST_SRP | + +-----------------------------+ + */ namespace con { class Connection; @@ -150,19 +173,25 @@ enum ClientState CS_Disconnecting, CS_Denied, CS_Created, - CS_InitSent, + CS_AwaitingInit2, + CS_HelloSent, CS_InitDone, CS_DefinitionsSent, - CS_Active + CS_Active, + CS_SudoMode }; enum ClientStateEvent { - CSE_Init, + CSE_Hello, + CSE_AuthAccept, + CSE_InitLegacy, CSE_GotInit2, CSE_SetDenied, CSE_SetDefinitionsSent, CSE_SetClientReady, + CSE_SudoSuccess, + CSE_SudoLeave, CSE_Disconnect }; @@ -201,10 +230,24 @@ public: // u16 net_proto_version; + /* Authentication information */ + std::string enc_pwd; + AuthMechanism chosen_mech; + void * auth_data; + u32 allowed_auth_mechs; + u32 allowed_sudo_mechs; + + bool isSudoMechAllowed(AuthMechanism mech) + { return allowed_sudo_mechs & mech; } + bool isMechAllowed(AuthMechanism mech) + { return allowed_auth_mechs & mech; } + RemoteClient(): peer_id(PEER_ID_INEXISTENT), serialization_version(SER_FMT_VER_INVALID), net_proto_version(0), + chosen_mech(AUTH_MECHANISM_NONE), + auth_data(NULL), m_time_from_building(9999), m_pending_serialization_version(SER_FMT_VER_INVALID), m_state(CS_Created), @@ -303,7 +346,6 @@ public: /* get uptime */ u32 uptime(); - /* set version information */ void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) { m_version_major = major; diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 556e8d0c0..3364de8c5 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -28,8 +28,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, // 0x01 { "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02 { "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03 - null_command_handler, // 0x04 - null_command_handler, // 0x05 + { "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04 + { "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05 null_command_handler, // 0x06 null_command_handler, // 0x07 null_command_handler, // 0x08 @@ -108,6 +108,19 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51 { "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52 { "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53 + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; @@ -116,7 +129,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { null_command_factory, // 0x00 null_command_factory, // 0x01 - null_command_factory, // 0x02 + { "TOSERVER_INIT", 1, false }, // 0x02 null_command_factory, // 0x03 null_command_factory, // 0x04 null_command_factory, // 0x05 @@ -129,7 +142,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x0c null_command_factory, // 0x0d null_command_factory, // 0x0e - { "TOSERVER_INIT", 1, false }, // 0x0F + null_command_factory, // 0x0F { "TOSERVER_INIT_LEGACY", 1, false }, // 0x10 { "TOSERVER_INIT2", 1, true }, // 0x11 null_command_factory, // 0x12 @@ -175,11 +188,26 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b { "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c - { "TOSERVER_PASSWORD", 0, true }, // 0x3d + null_command_factory, // 0x3d null_command_factory, // 0x3e null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 { "TOSERVER_BREATH", 0, true }, // 0x42 { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 + null_command_factory, // 0x44 + null_command_factory, // 0x45 + null_command_factory, // 0x46 + null_command_factory, // 0x47 + null_command_factory, // 0x48 + null_command_factory, // 0x49 + null_command_factory, // 0x4a + null_command_factory, // 0x4b + null_command_factory, // 0x4c + null_command_factory, // 0x4d + null_command_factory, // 0x4e + null_command_factory, // 0x4f + { "TOSERVER_FIRST_SRP", 1, true }, // 0x50 + { "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51 + { "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52 }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index bddf8f6fd..2106e4368 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "strfnd.h" #include "network/clientopcodes.h" #include "util/serialize.h" +#include "util/srp.h" void Client::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -44,10 +45,16 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) return; u8 deployed; - *pkt >> deployed; + u32 auth_mechs; + std::string username_legacy; // for case insensitivity + *pkt >> deployed >> auth_mechs >> username_legacy; + + // Chose an auth method we support + AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs); infostream << "Client: TOCLIENT_HELLO received with " - "deployed=" << ((int)deployed & 0xff) << std::endl; + "deployed=" << ((int)deployed & 0xff) << ", auth_mechs=" + << auth_mechs << ", chosen=" << chosen_auth_mechanism << std::endl; if (!ser_ver_supported(deployed)) { infostream << "Client: TOCLIENT_HELLO: Server sent " @@ -56,14 +63,43 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) } m_server_ser_ver = deployed; + m_proto_ver = deployed; + + //TODO verify that username_legacy matches sent username, only + // differs in casing (make both uppercase and compare) + // This is only neccessary though when we actually want to add casing support + + if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) { + // we recieved a TOCLIENT_HELLO while auth was already going on + errorstream << "Client: TOCLIENT_HELLO while auth was already going on" + << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl; + if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP) + || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) { + srp_user_delete((SRPUser *) m_auth_data); + m_auth_data = 0; + } + } + + // Authenticate using that method, or abort if there wasn't any method found + if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) { + startAuth(chosen_auth_mechanism); + } else { + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + m_access_denied = true; + m_access_denied_reason = "Unknown"; + m_con.Disconnect(); + } - // @ TODO auth to server } void Client::handleCommand_AuthAccept(NetworkPacket* pkt) { + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + deleteAuthData(); + v3f playerpos; - *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval; + *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval + >> m_sudo_auth_methods; playerpos -= v3f(0, BS / 2, 0); @@ -82,7 +118,28 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt) m_state = LC_Init; } +void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt) +{ + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + deleteAuthData(); + + m_password = m_new_password; + + verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl; + // send packet to actually set the password + startAuth(AUTH_MECHANISM_FIRST_SRP); + + // reset again + m_chosen_auth_mech = AUTH_MECHANISM_NONE; +} +void Client::handleCommand_DenySudoMode(NetworkPacket* pkt) +{ + m_chat_queue.push(L"Password change denied. Password NOT changed."); + // reset everything and be sad + deleteAuthData(); + m_chosen_auth_mech = AUTH_MECHANISM_NONE; +} void Client::handleCommand_InitLegacy(NetworkPacket* pkt) { if (pkt->getSize() < 1) @@ -101,6 +158,7 @@ void Client::handleCommand_InitLegacy(NetworkPacket* pkt) } m_server_ser_ver = deployed; + m_proto_ver = deployed; // Get player position v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0); @@ -1105,3 +1163,36 @@ void Client::handleCommand_EyeOffset(NetworkPacket* pkt) *pkt >> player->eye_offset_first >> player->eye_offset_third; } + +void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt) +{ + if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) + && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) { + errorstream << "Client: Recieved SRP S_B login message," + << " but wasn't supposed to (chosen_mech=" + << m_chosen_auth_mech << ")." << std::endl; + return; + } + + char *bytes_M = 0; + size_t len_M = 0; + SRPUser *usr = (SRPUser *) m_auth_data; + std::string s; + std::string B; + *pkt >> s >> B; + + infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl; + + srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(), + (const unsigned char *) B.c_str(), B.size(), + (unsigned char **) &bytes_M, &len_M); + + if ( !bytes_M ) { + errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl; + return; + } + + NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0); + resp_pkt << std::string(bytes_M, len_M); + Send(&resp_pkt); +} diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 0d2015e7f..0408b9cac 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -41,8 +41,10 @@ public: u16 getPeerId() { return m_peer_id; } u16 getCommand() { return m_command; } - // Data extractors + // Returns a c-string without copying. + // A better name for this would be getRawString() char* getString(u32 from_offset); + // major difference to putCString(): doesn't write len into the buffer void putRawString(const char* src, u32 len); NetworkPacket& operator>>(std::string& dst); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ba934957d..ba12a206e 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -120,11 +120,15 @@ with this program; if not, write to the Free Software Foundation, Inc., permit translation Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and reading u32 - Add TOSERVER_INIT new opcode (0x02) for client presentation to server - Add TOSERVER_AUTH new opcode (0x03) for client authentication + Add new opcode TOSERVER_INIT for client presentation to server + Add new opcodes TOSERVER_FIRST_SRP, TOSERVER_SRP_BYTES_A, + TOSERVER_SRP_BYTES_M, TOCLIENT_SRP_BYTES_S_B + for the three supported auth mechanisms around srp + Add new opcodes TOCLIENT_ACCEPT_SUDO_MODE and TOCLIENT_DENY_SUDO_MODE + for sudo mode handling (auth mech generic way of changing password). Add TOCLIENT_HELLO for presenting server to client after client presentation - Add TOCLIENT_AUTH_ACCEPT to accept connexion from client + Add TOCLIENT_AUTH_ACCEPT to accept connection from client */ #define LATEST_PROTOCOL_VERSION 24 @@ -151,14 +155,31 @@ with this program; if not, write to the Free Software Foundation, Inc., enum ToClientCommand { TOCLIENT_HELLO = 0x02, - TOCLIENT_AUTH_ACCEPT = 0x03, - TOCLIENT_ACCESS_DENIED = 0x0A, /* - u16 command - u16 reason_length - wstring reason + Sent after TOSERVER_INIT. + + u8 deployed version + u32 supported auth methods + std::string username that should be used for legacy hash (for proper casing) */ + TOCLIENT_AUTH_ACCEPT = 0x03, + /* + Message from server to accept auth. + v3s16 player's position + v3f(0,BS/2,0) floatToInt'd + u64 map seed + f1000 recommended send interval + u32 : supported auth methods for sudo mode + (where the user can change their password) + */ + TOCLIENT_ACCEPT_SUDO_MODE = 0x04, + /* + Sent to client to show it is in sudo mode now. + */ + TOCLIENT_DENY_SUDO_MODE = 0x05, + /* + Signals client that sudo mode auth failed. + */ TOCLIENT_INIT_LEGACY = 0x10, /* Server's reply to TOSERVER_INIT. @@ -173,7 +194,11 @@ enum ToClientCommand NOTE: The position in here is deprecated; position is explicitly sent afterwards */ - + TOCLIENT_ACCESS_DENIED = 0x0A, + /* + u8 reason + std::string custom reason (if reason == SERVER_ACCESSDENIED_CUSTOM_STRING) + */ TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks TOCLIENT_ADDNODE = 0x21, /* @@ -589,7 +614,16 @@ enum ToClientCommand u32 id */ - TOCLIENT_NUM_MSG_TYPES = 0x54, + TOCLIENT_SRP_BYTES_S_B = 0x60, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP. + + u16 command + std::string bytes_s + std::string bytes_B + */ + + TOCLIENT_NUM_MSG_TYPES = 0x61, }; enum ToServerCommand @@ -598,18 +632,11 @@ enum ToServerCommand /* Sent first after connected. - [0] u16 TOSERVER_INIT [2] u8 SER_FMT_VER_HIGHEST_READ [3] u8 compression_modes - */ - - TOSERVER_AUTH = 0x03, - /* - Sent first after presentation (INIT). - [0] std::string player_name - [0+*] std::string password (new in some version) - [0+*+*] u16 minimum supported network protocol version (added sometime) - [0+*+*+2] u16 maximum supported network protocol version (added later than the previous one) + [4] u16 minimum supported network protocol version + [6] u16 maximum supported network protocol version + [8] std::string player name */ TOSERVER_INIT_LEGACY = 0x10, @@ -817,15 +844,6 @@ enum ToServerCommand u8[len] field value */ - TOSERVER_PASSWORD = 0x3d, - /* - Sent to change password. - - [0] u16 TOSERVER_PASSWORD - [2] std::string old password - [2+*] std::string new password - */ - TOSERVER_REQUEST_MEDIA = 0x40, /* u16 command @@ -857,7 +875,49 @@ enum ToServerCommand u8[len] full_version_string */ - TOSERVER_NUM_MSG_TYPES = 0x44, + TOSERVER_FIRST_SRP = 0x50, + /* + Belonging to AUTH_MECHANISM_FIRST_SRP. + + std::string srp salt + std::string srp verification key + u8 is_empty (=1 if password is empty, 0 otherwise) + */ + + TOSERVER_SRP_BYTES_A = 0x51, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP, + depending on current_login_based_on. + + std::string bytes_A + u8 current_login_based_on : on which version of the password's + hash this login is based on (0 legacy hash, + or 1 directly the password) + */ + + TOSERVER_SRP_BYTES_M = 0x52, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP. + + std::string bytes_M + */ + + TOSERVER_NUM_MSG_TYPES = 0x53, +}; + +enum AuthMechanism +{ + // reserved + AUTH_MECHANISM_NONE = 0, + + // SRP based on the legacy hash + AUTH_MECHANISM_LEGACY_PASSWORD = 1 << 0, + + // SRP based on the srp verification key + AUTH_MECHANISM_SRP = 1 << 1, + + // Establishes a srp verification key, for first login and password changing + AUTH_MECHANISM_FIRST_SRP = 1 << 2, }; enum AccessDeniedCode { diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 9c1da9ad1..92d24fe40 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -26,8 +26,8 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = { null_command_handler, // 0x00 (never use this) null_command_handler, // 0x01 - { "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02 - { "TOSERVER_AUTH", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Auth }, // 0x03 + { "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02 + null_command_handler, // 0x03 null_command_handler, // 0x04 null_command_handler, // 0x05 null_command_handler, // 0x06 @@ -85,13 +85,28 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b { "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c - { "TOSERVER_PASSWORD", TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x3d + null_command_handler, // 0x3d null_command_handler, // 0x3e null_command_handler, // 0x3f { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41 { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42 { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43 + null_command_handler, // 0x44 + null_command_handler, // 0x45 + null_command_handler, // 0x46 + null_command_handler, // 0x47 + null_command_handler, // 0x48 + null_command_handler, // 0x49 + null_command_handler, // 0x4a + null_command_handler, // 0x4b + null_command_handler, // 0x4c + null_command_handler, // 0x4d + null_command_handler, // 0x4e + null_command_handler, // 0x4f + { "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50 + { "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51 + { "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52 }; const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false }; @@ -100,10 +115,10 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { null_command_factory, // 0x00 null_command_factory, // 0x01 - { "TOCLIENT_HELLO", 0, true }, // 0x02 + null_command_factory, // 0x02 { "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03 - null_command_factory, // 0x04 - null_command_factory, // 0x05 + { "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04 + { "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05 null_command_factory, // 0x06 null_command_factory, // 0x07 null_command_factory, // 0x08 @@ -182,4 +197,17 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51 { "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52 { "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53 + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 }; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 6b1d5bc02..a6f5ffca1 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -32,9 +32,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" +#include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" #include "util/serialize.h" +#include "util/srp.h" void Server::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -92,13 +94,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt) u8 compression_modes; u16 min_net_proto_version = 0; u16 max_net_proto_version; + std::string playerName; *pkt >> client_max >> compression_modes >> min_net_proto_version - >> max_net_proto_version; + >> max_net_proto_version >> playerName; u8 our_max = SER_FMT_VER_HIGHEST_READ; // Use the highest version supported by both - int deployed = std::min(client_max, our_max); + u8 deployed = std::min(client_max, our_max); // If it's lower than the lowest supported, give up. if (deployed < SER_FMT_VER_LOWEST) deployed = SER_FMT_VER_INVALID; @@ -137,7 +140,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt) client->net_proto_version = net_proto_version; - // On this handler protocol version 25 is required + // On this handler at least protocol version 25 is required if (net_proto_version < 25 || net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { @@ -156,36 +159,9 @@ void Server::handleCommand_Init(NetworkPacket* pkt) } } - // @TODO: check if we support same modes, but not required now - - client->setSupportedCompressionModes(compression_modes); - - m_clients.event(pkt->getPeerId(), CSE_Init); -} - -void Server::handleCommand_Auth(NetworkPacket* pkt) -{ - std::string addr_s; - try { - Address address = getPeerAddress(pkt->getPeerId()); - addr_s = address.serializeString(); - } - catch (con::PeerNotFoundException &e) { - /* - * no peer for this packet found - * most common reason is peer timeout, e.g. peer didn't - * respond for some time, your server was overloaded or - * things like that. - */ - infostream << "Server::ProcessData(): Canceling: peer " - << pkt->getPeerId() << " not found" << std::endl; - return; - } - - std::string playerName, playerPassword; - - *pkt >> playerName >> playerPassword; - + /* + Validate player name + */ const char* playername = playerName.c_str(); if (playerName.size() > PLAYERNAME_SIZE) { @@ -202,6 +178,11 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) return; } + m_clients.setPlayerName(pkt->getPeerId(), playername); + //TODO (later) case insensitivity + + std::string legacyPlayerNameCasing = playerName; + if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) { actionstream << "Server: Player with the name \"singleplayer\" " << "tried to connect from " << addr_s << std::endl; @@ -222,23 +203,9 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) } } - if (playerPassword.size() > PASSWORD_SIZE) { - actionstream << "Server: Player with an too long password " - << "tried to connect from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - infostream << "Server: New connection: \"" << playerName << "\" from " << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; - if(!base64_is_valid(playerPassword)){ - actionstream << "Server: " << playerName - << " supplied invalid password hash" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - // Enforce user limit. // Don't enforce for users that have some admin right if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") && @@ -247,76 +214,74 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) !checkPriv(playername, "privs") && !checkPriv(playername, "password") && playername != g_settings->get("name")) { - actionstream << "Server: " << playername << " tried to join, but there" - << " are already max_users=" + actionstream << "Server: " << playername << " tried to join from " + << addr_s << ", but there" << " are already max_users=" << g_settings->getU16("max_users") << " players." << std::endl; DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS); return; } - std::string checkpwd; // Password hash to check against - bool has_auth = m_script->getAuth(playername, &checkpwd, NULL); - - // If no authentication info exists for user, create it - if (!has_auth) { - if (!isSingleplayer() && - g_settings->getBool("disallow_empty_password") && - playerPassword.empty()) { - actionstream << "Server: " << playerName - << " supplied empty password" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD); + /* + Compose auth methods for answer + */ + std::string encpwd; // encrypted Password field for the user + bool has_auth = m_script->getAuth(playername, &encpwd, NULL); + u32 auth_mechs = 0; + + client->chosen_mech = AUTH_MECHANISM_NONE; + + if (has_auth) { + std::vector<std::string> pwd_components = str_split(encpwd, '#'); + if (pwd_components.size() == 4) { + if (pwd_components[1] == "1") { // 1 means srp + auth_mechs |= AUTH_MECHANISM_SRP; + client->enc_pwd = encpwd; + } else { + actionstream << "User " << playername + << " tried to log in, but password field" + << " was invalid (unknown mechcode)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + return; + } + } else if (base64_is_valid(encpwd)) { + auth_mechs |= AUTH_MECHANISM_LEGACY_PASSWORD; + client->enc_pwd = encpwd; + } else { + actionstream << "User " << playername + << " tried to log in, but password field" + << " was invalid (invalid base64)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); return; } - std::string raw_default_password = - g_settings->get("default_password"); - std::string initial_password = - translatePassword(playername, raw_default_password); - - // If default_password is empty, allow any initial password - if (raw_default_password.length() == 0) - initial_password = playerPassword.c_str(); - - m_script->createAuth(playername, initial_password); - } - - has_auth = m_script->getAuth(playername, &checkpwd, NULL); - - if(!has_auth) { - actionstream << "Server: " << playerName << " cannot be authenticated" - << " (auth handler does not work?)" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); - return; - } - - if(playerPassword.c_str() != checkpwd) { - actionstream << "Server: " << playerName << " supplied wrong password" - << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - - RemotePlayer *player = - static_cast<RemotePlayer*>(m_env->getPlayer(playername)); - - if (player && player->peer_id != 0) { - errorstream << "Server: " << playername << ": Failed to emerge player" - << " (player allocated to an another client)" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_ALREADY_CONNECTED); + } else { + std::string default_password = g_settings->get("default_password"); + if (default_password.length() == 0) { + auth_mechs |= AUTH_MECHANISM_FIRST_SRP; + } else { + // Take care of default passwords. + client->enc_pwd = getSRPVerifier(playerName, default_password); + auth_mechs |= AUTH_MECHANISM_SRP; + } } - m_clients.setPlayerName(pkt->getPeerId(), playername); - /* - Answer with a TOCLIENT_INIT + Answer with a TOCLIENT_HELLO */ - NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, pkt->getPeerId()); + verbosestream << "Sending TOCLIENT_HELLO with auth method field: " + << auth_mechs << std::endl; - resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed() - << g_settings->getFloat("dedicated_server_step"); + NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4 + + legacyPlayerNameCasing.size(), pkt->getPeerId()); + + resp_pkt << deployed << auth_mechs << legacyPlayerNameCasing; Send(&resp_pkt); - m_clients.event(pkt->getPeerId(), CSE_Init); + + client->allowed_auth_mechs = auth_mechs; + client->setSupportedCompressionModes(compression_modes); + + m_clients.event(pkt->getPeerId(), CSE_Hello); } void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) @@ -354,7 +319,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) return; } - verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id=" + verbosestream << "Server: Got TOSERVER_INIT_LEGACY from " << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; // Do not allow multiple players in simple singleplayer mode. @@ -422,6 +387,10 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) net_proto_version = max_net_proto_version; } + // The client will send up to date init packet, ignore this one + if (net_proto_version >= 25) + return; + verbosestream << "Server: " << addr_s << ": Protocol version: min: " << min_net_proto_version << ", max: " << max_net_proto_version << ", chosen: " << net_proto_version << std::endl; @@ -624,7 +593,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) << g_settings->getFloat("dedicated_server_step"); Send(&resp_pkt); - m_clients.event(pkt->getPeerId(), CSE_Init); + m_clients.event(pkt->getPeerId(), CSE_InitLegacy); } void Server::handleCommand_Init2(NetworkPacket* pkt) @@ -1223,33 +1192,33 @@ void Server::handleCommand_Breath(NetworkPacket* pkt) void Server::handleCommand_Password(NetworkPacket* pkt) { - if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) || - pkt->getSize() != PASSWORD_SIZE * 2) + if (pkt->getSize() != PASSWORD_SIZE * 2) return; std::string oldpwd; std::string newpwd; - if (pkt->getCommand() == TOSERVER_PASSWORD) { - *pkt >> oldpwd >> newpwd; + // Deny for clients using the new protocol + RemoteClient* client = getClient(pkt->getPeerId(), CS_Created); + if (client->net_proto_version >= 25) { + infostream << "Server::handleCommand_Password(): Denying change: " + << " Client protocol version for peer_id=" << pkt->getPeerId() + << " too new!" << std::endl; + return; } - // 13/03/15 - // Protocol v24 compat. Please remove in 1 year after - // client convergence to 0.4.13/0.5.x - else { - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(i); - if (c == 0) - break; - oldpwd += c; - } - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(PASSWORD_SIZE + i); - if (c == 0) - break; - newpwd += c; - } + for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { + char c = pkt->getChar(i); + if (c == 0) + break; + oldpwd += c; + } + + for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { + char c = pkt->getChar(PASSWORD_SIZE + i); + if (c == 0) + break; + newpwd += c; } Player *player = m_env->getPlayer(pkt->getPeerId()); @@ -1848,3 +1817,258 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt) m_script->on_playerReceiveFields(playersao, formname, fields); } + +void Server::handleCommand_FirstSrp(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + std::string playername = client->getName(); + + std::string salt; + std::string verification_key; + + std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString(); + u8 is_empty; + + *pkt >> salt >> verification_key >> is_empty; + + verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s + << ", with is_empty= " << is_empty << std::endl; + + // Either this packet is sent because the user is new or to change the password + if (cstate == CS_HelloSent) { + if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) { + actionstream << "Server: Client from " << addr_s + << " tried to set password without being " + << "authenticated, or the username being new." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + + if (!isSingleplayer() && + g_settings->getBool("disallow_empty_password") && + is_empty == 1) { + actionstream << "Server: " << playername + << " supplied empty password from " << addr_s << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD); + return; + } + + std::string initial_ver_key; + std::string raw_default_password = g_settings->get("default_password"); + // If default_password is empty, allow any initial password + if (raw_default_password.length() == 0) { + initial_ver_key = encodeSRPVerifier(verification_key, salt); + } else { + initial_ver_key = getSRPVerifier(playername, raw_default_password); + } + + m_script->createAuth(playername, initial_ver_key); + + acceptAuth(pkt->getPeerId(), false); + } else { + if (cstate < CS_SudoMode) { + infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from " + << addr_s << ": " << "Client has wrong state " << cstate << "." + << std::endl; + return; + } + m_clients.event(pkt->getPeerId(), CSE_SudoLeave); + std::string pw_db_field = encodeSRPVerifier(verification_key, salt); + bool success = m_script->setPassword(playername, pw_db_field); + if (success) { + actionstream << playername << " changes password" << std::endl; + SendChatMessage(pkt->getPeerId(), L"Password change successful."); + } else { + actionstream << playername << " tries to change password but " + << "it fails" << std::endl; + SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable."); + } + } +} + +void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + bool wantSudo = (cstate == CS_Active); + + if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { + actionstream << "Server: got SRP _A packet in wrong state " + << cstate << " from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << ". Ignoring." << std::endl; + return; + } + + if (client->chosen_mech != AUTH_MECHANISM_NONE) { + actionstream << "Server: got SRP _A packet, while auth" + << "is already going on with mech " << client->chosen_mech + << " from " << getPeerAddress(pkt->getPeerId()).serializeString() + << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + std::string bytes_A; + u8 based_on; + *pkt >> bytes_A >> based_on; + + infostream << "Server: TOSERVER_SRP_BYTES_A received with " + << "based_on=" << int(based_on) << " and len_A=" + << bytes_A.length() << "." << std::endl; + + AuthMechanism chosen = (based_on == 0) ? + AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP; + + if (wantSudo) { + if (!client->isSudoMechAllowed(chosen)) { + actionstream << "Server: Player \"" << client->getName() + << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " tried to change password using unallowed mech " + << chosen << "." << std::endl; + DenySudoAccess(pkt->getPeerId()); + return; + } + } else { + if (!client->isMechAllowed(chosen)) { + actionstream << "Server: Client tried to authenticate from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << " using unallowed mech " << chosen << "." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + client->chosen_mech = chosen; + + std::string bytes_s; + std::string bytes_v; + + if (based_on == 0) { + char *p_bytes_s = 0; + size_t len_s = 0; + char *p_bytes_v = 0; + size_t len_v = 0; + getSRPVerifier(client->getName(), client->enc_pwd, + &p_bytes_s, &len_s, + &p_bytes_v, &len_v); + bytes_s = std::string(p_bytes_s, len_s); + bytes_v = std::string(p_bytes_v, len_v); + free(p_bytes_s); + free(p_bytes_v); + } else if (!decodeSRPVerifier(client->enc_pwd, &bytes_s, &bytes_v)) { + // Non-base64 errors should have been catched in the init handler + actionstream << "Server: User " << client->getName() + << " tried to log in, but srp verifier field" + << " was invalid (most likely invalid base64)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + return; + } + + char *bytes_B = 0; + size_t len_B = 0; + + client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048, + client->getName().c_str(), + (const unsigned char *) bytes_s.c_str(), bytes_s.size(), + (const unsigned char *) bytes_v.c_str(), bytes_v.size(), + (const unsigned char *) bytes_A.c_str(), bytes_A.size(), + NULL, 0, + (unsigned char **) &bytes_B, &len_B, NULL, NULL); + + if (!bytes_B) { + actionstream << "Server: User " << client->getName() + << " tried to log in, SRP-6a safety check violated in _A handler." + << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId()); + resp_pkt << bytes_s << std::string(bytes_B, len_B); + Send(&resp_pkt); +} + +void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + bool wantSudo = (cstate == CS_Active); + + verbosestream << "Server: Recieved TOCLIENT_SRP_BYTES_M." << std::endl; + + if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { + actionstream << "Server: got SRP _M packet in wrong state " + << cstate << " from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << ". Ignoring." << std::endl; + return; + } + + if ((client->chosen_mech != AUTH_MECHANISM_SRP) + && (client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD)) { + actionstream << "Server: got SRP _M packet, while auth" + << "is going on with mech " << client->chosen_mech + << " from " << getPeerAddress(pkt->getPeerId()).serializeString() + << " (wantSudo=" << wantSudo << "). Denying." << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + std::string bytes_M; + *pkt >> bytes_M; + + if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data) + != bytes_M.size()) { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " sent bytes_M with invalid length " << bytes_M.size() << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + + unsigned char *bytes_HAMK = 0; + + srp_verifier_verify_session((SRPVerifier *) client->auth_data, + (unsigned char *)bytes_M.c_str(), &bytes_HAMK); + + if (!bytes_HAMK) { + if (wantSudo) { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " tried to change their password, but supplied wrong" + << " (SRP) password for authentication." << std::endl; + DenySudoAccess(pkt->getPeerId()); + return; + } else { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " supplied wrong (SRP) password from address " + << getPeerAddress(pkt->getPeerId()).serializeString() + << "." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); + return; + } + } + + acceptAuth(pkt->getPeerId(), wantSudo); +} diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 0464f46d9..283cca01f 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "filesys.h" #include "settings.h" +#include "util/auth.h" // debug(...) // Writes a line to dstream diff --git a/src/server.cpp b/src/server.cpp index 7289dc905..f032da406 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2537,6 +2537,13 @@ void Server::RespawnPlayer(u16 peer_id) playersao->setPos(pos); } } +void Server::DenySudoAccess(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + + NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id); + Send(&pkt); +} void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason) { @@ -2558,6 +2565,37 @@ void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason) m_con.DisconnectPeer(peer_id); } +void Server::acceptAuth(u16 peer_id, bool forSudoMode) +{ + DSTACK(__FUNCTION_NAME); + + if (!forSudoMode) { + RemoteClient* client = getClient(peer_id, CS_Invalid); + + NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id); + + // Right now, the auth mechs don't change between login and sudo mode. + u32 sudo_auth_mechs = client->allowed_auth_mechs; + client->allowed_sudo_mechs = sudo_auth_mechs; + + resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed() + << g_settings->getFloat("dedicated_server_step") + << sudo_auth_mechs; + + Send(&resp_pkt); + m_clients.event(peer_id, CSE_AuthAccept); + } else { + NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id); + + // We only support SRP right now + u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP; + + resp_pkt << sudo_auth_mechs; + Send(&resp_pkt); + m_clients.event(peer_id, CSE_SudoSuccess); + } +} + void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) { DSTACK(__FUNCTION_NAME); diff --git a/src/server.h b/src/server.h index 25ffb7fc6..f53e23a3a 100644 --- a/src/server.h +++ b/src/server.h @@ -193,7 +193,6 @@ public: void handleCommand_Null(NetworkPacket* pkt) {}; void handleCommand_Deprecated(NetworkPacket* pkt); void handleCommand_Init(NetworkPacket* pkt); - void handleCommand_Auth(NetworkPacket* pkt); void handleCommand_Init_Legacy(NetworkPacket* pkt); void handleCommand_Init2(NetworkPacket* pkt); void handleCommand_RequestMedia(NetworkPacket* pkt); @@ -213,6 +212,9 @@ public: void handleCommand_RemovedSounds(NetworkPacket* pkt); void handleCommand_NodeMetaFields(NetworkPacket* pkt); void handleCommand_InventoryFields(NetworkPacket* pkt); + void handleCommand_FirstSrp(NetworkPacket* pkt); + void handleCommand_SrpBytesA(NetworkPacket* pkt); + void handleCommand_SrpBytesM(NetworkPacket* pkt); void ProcessData(NetworkPacket *pkt); @@ -360,7 +362,9 @@ public: void peerAdded(con::Peer *peer); void deletingPeer(con::Peer *peer, bool timeout); + void DenySudoAccess(u16 peer_id); void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason=""); + void acceptAuth(u16 peer_id, bool forSudoMode); void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason); bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval); bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime, diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 112f09bb9..33900a43a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,11 +1,14 @@ set(UTIL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sha256.c ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp PARENT_SCOPE) diff --git a/src/util/auth.cpp b/src/util/auth.cpp new file mode 100644 index 000000000..ac5139535 --- /dev/null +++ b/src/util/auth.cpp @@ -0,0 +1,126 @@ +/* +Minetest +Copyright (C) 2015 est31 <MTest31@outlook.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include <algorithm> +#include <string> +#include "auth.h" +#include "base64.h" +#include "sha1.h" +#include "srp.h" +#include "string.h" + +// Get an sha-1 hash of the player's name combined with +// the password entered. That's what the server uses as +// their password. (Exception : if the password field is +// blank, we send a blank password - this is for backwards +// compatibility with password-less players). +std::string translatePassword(const std::string &name, + const std::string &password) +{ + if (password.length() == 0) + return ""; + + std::string slt = name + password; + SHA1 sha1; + sha1.addBytes(slt.c_str(), slt.length()); + unsigned char *digest = sha1.getDigest(); + std::string pwd = base64_encode(digest, 20); + free(digest); + return pwd; +} + +void getSRPVerifier(const std::string &name, + const std::string &password, char **salt, size_t *salt_len, + char **bytes_v, size_t *len_v) +{ + std::string n_name = lowercase(name); + srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048, + n_name.c_str(), (const unsigned char *)password.c_str(), + password.size(), (unsigned char **)salt, salt_len, + (unsigned char **)bytes_v, len_v, NULL, NULL); +} + +// Get a db-ready SRP verifier +// The salt param is only modifyable by this method so that you can free it +// if it was allocated. You shouldn't use it for other purposes, as you will +// need the contents of salt_len too. +inline static std::string getSRPVerifier(const std::string &name, + const std::string &password, char ** salt, size_t salt_len) +{ + char * bytes_v = NULL; + size_t len_v; + getSRPVerifier(name, password, salt, &salt_len, + &bytes_v, &len_v); + std::string ret_val = encodeSRPVerifier(std::string(bytes_v, len_v), + std::string(*salt, salt_len)); + free(bytes_v); + return ret_val; +} + +// Get a db-ready SRP verifier +std::string getSRPVerifier(const std::string &name, + const std::string &password) +{ + char * salt = NULL; + std::string ret_val = getSRPVerifier(name, + password, &salt, 0); + free(salt); + return ret_val; +} + +// Get a db-ready SRP verifier +std::string getSRPVerifier(const std::string &name, + const std::string &password, const std::string &salt) +{ + // The implementation won't change the salt if its set, + // therefore we can cast. + char *salt_cstr = (char *)salt.c_str(); + return getSRPVerifier(name, password, + &salt_cstr, salt.size()); +} + +// Make a SRP verifier db-ready +std::string encodeSRPVerifier(const std::string &verifier, + const std::string &salt) +{ + std::ostringstream ret_str; + ret_str << "#1#" + << base64_encode((unsigned char*) salt.c_str(), salt.size()) << "#" + << base64_encode((unsigned char*) verifier.c_str(), verifier.size()); + return ret_str.str(); +} + +bool decodeSRPVerifier(const std::string &enc_pwd, + std::string *salt, std::string *bytes_v) +{ + std::vector<std::string> pwd_components = str_split(enc_pwd, '#'); + + if ((pwd_components.size() != 4) + || (pwd_components[1] != "1") // 1 means srp + || !base64_is_valid(pwd_components[2]) + || !base64_is_valid(pwd_components[3])) + return false; + + std::string salt_str = base64_decode(pwd_components[2]); + std::string bytes_v_str = base64_decode(pwd_components[3]); + *salt = salt_str; + *bytes_v = bytes_v_str; + return true; + +} diff --git a/src/util/auth.h b/src/util/auth.h new file mode 100644 index 000000000..36d8c20a4 --- /dev/null +++ b/src/util/auth.h @@ -0,0 +1,37 @@ +/* +Minetest +Copyright (C) 2015 est31 <MTest31@outlook.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef AUTH_H +#define AUTH_H + +std::string translatePassword(const std::string &name, + const std::string &password); +void getSRPVerifier(const std::string &name, + const std::string &password, char **salt, size_t *salt_len, + char **bytes_v, size_t *len_v); +std::string getSRPVerifier(const std::string &name, + const std::string &password); +std::string getSRPVerifier(const std::string &name, + const std::string &password, const std::string &salt); +std::string encodeSRPVerifier(const std::string &verifier, + const std::string &salt); +bool decodeSRPVerifier(const std::string &enc_pwd, + std::string *salt, std::string *bytes_v); + +#endif
\ No newline at end of file diff --git a/src/util/md32_common.h b/src/util/md32_common.h new file mode 100644 index 000000000..085d1d7a5 --- /dev/null +++ b/src/util/md32_common.h @@ -0,0 +1,428 @@ +/* md32_common.h file used by sha256 implementation */ +/* ==================================================================== + * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + */ + +/*- + * This is a generic 32 bit "collector" for message digest algorithms. + * Whenever needed it collects input character stream into chunks of + * 32 bit values and invokes a block function that performs actual hash + * calculations. + * + * Porting guide. + * + * Obligatory macros: + * + * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN + * this macro defines byte order of input stream. + * HASH_CBLOCK + * size of a unit chunk HASH_BLOCK operates on. + * HASH_LONG + * has to be at lest 32 bit wide, if it's wider, then + * HASH_LONG_LOG2 *has to* be defined along + * HASH_CTX + * context structure that at least contains following + * members: + * typedef struct { + * ... + * HASH_LONG Nl,Nh; + * either { + * HASH_LONG data[HASH_LBLOCK]; + * unsigned char data[HASH_CBLOCK]; + * }; + * unsigned int num; + * ... + * } HASH_CTX; + * data[] vector is expected to be zeroed upon first call to + * HASH_UPDATE. + * HASH_UPDATE + * name of "Update" function, implemented here. + * HASH_TRANSFORM + * name of "Transform" function, implemented here. + * HASH_FINAL + * name of "Final" function, implemented here. + * HASH_BLOCK_DATA_ORDER + * name of "block" function capable of treating *unaligned* input + * message in original (data) byte order, implemented externally. + * HASH_MAKE_STRING + * macro convering context variables to an ASCII hash string. + * + * MD5 example: + * + * #define DATA_ORDER_IS_LITTLE_ENDIAN + * + * #define HASH_LONG MD5_LONG + * #define HASH_LONG_LOG2 MD5_LONG_LOG2 + * #define HASH_CTX MD5_CTX + * #define HASH_CBLOCK MD5_CBLOCK + * #define HASH_UPDATE MD5_Update + * #define HASH_TRANSFORM MD5_Transform + * #define HASH_FINAL MD5_Final + * #define HASH_BLOCK_DATA_ORDER md5_block_data_order + * + * <appro@fy.chalmers.se> + */ + +#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) +# error "DATA_ORDER must be defined!" +#endif + +#ifndef HASH_CBLOCK +# error "HASH_CBLOCK must be defined!" +#endif +#ifndef HASH_LONG +# error "HASH_LONG must be defined!" +#endif +#ifndef HASH_CTX +# error "HASH_CTX must be defined!" +#endif + +#ifndef HASH_UPDATE +# error "HASH_UPDATE must be defined!" +#endif +#ifndef HASH_TRANSFORM +# error "HASH_TRANSFORM must be defined!" +#endif +#ifndef HASH_FINAL +# error "HASH_FINAL must be defined!" +#endif + +#ifndef HASH_BLOCK_DATA_ORDER +# error "HASH_BLOCK_DATA_ORDER must be defined!" +#endif + +/* + * Engage compiler specific rotate intrinsic function if available. + */ +#undef ROTATE +#ifndef PEDANTIC +# if defined(_MSC_VER) +# define ROTATE(a,n) _lrotl(a,n) +# elif defined(__ICC) +# define ROTATE(a,n) _rotl(a,n) +# elif defined(__MWERKS__) +# if defined(__POWERPC__) +# define ROTATE(a,n) __rlwinm(a,n,0,31) +# elif defined(__MC68K__) + /* Motorola specific tweak. <appro@fy.chalmers.se> */ +# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) +# else +# define ROTATE(a,n) __rol(a,n) +# endif +# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) + /* + * Some GNU C inline assembler templates. Note that these are + * rotates by *constant* number of bits! But that's exactly + * what we need here... + * <appro@fy.chalmers.se> + */ +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "roll %1,%0" \ + : "=r"(ret) \ + : "I"(n), "0"((unsigned int)(a)) \ + : "cc"); \ + ret; \ + }) +# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ + defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ( \ + "rlwinm %0,%1,%2,0,31" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# elif defined(__s390x__) +# define ROTATE(a,n) ({ register unsigned int ret; \ + asm ("rll %0,%1,%2" \ + : "=r"(ret) \ + : "r"(a), "I"(n)); \ + ret; \ + }) +# endif +# endif +#endif /* PEDANTIC */ + +#ifndef ROTATE +# define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) +#endif + +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + +# ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \ + (defined(__x86_64) || defined(__x86_64__)) +# if !defined(B_ENDIAN) + /* + * This gives ~30-40% performance improvement in SHA-256 compiled + * with gcc [on P4]. Well, first macro to be frank. We can pull + * this trick on x86* platforms only, because these CPUs can fetch + * unaligned data without raising an exception. + */ +# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + (c)+=4; (l)=r; }) +# define HOST_l2c(l,c) ({ unsigned int r=(l); \ + asm ("bswapl %0":"=r"(r):"0"(r)); \ + *((unsigned int *)(c))=r; (c)+=4; r; }) +# endif +# elif defined(__aarch64__) +# if defined(__BYTE_ORDER__) +# if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ +# define HOST_c2l(c,l) ({ unsigned int r; \ + asm ("rev %w0,%w1" \ + :"=r"(r) \ + :"r"(*((const unsigned int *)(c))));\ + (c)+=4; (l)=r; }) +# define HOST_l2c(l,c) ({ unsigned int r; \ + asm ("rev %w0,%w1" \ + :"=r"(r) \ + :"r"((unsigned int)(l)));\ + *((unsigned int *)(c))=r; (c)+=4; r; }) +# elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l)) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l)) +# endif +# endif +# endif +# endif +# if defined(__s390__) || defined(__s390x__) +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l)) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l)) +# endif +# endif + +# ifndef HOST_c2l +# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++))) ) ) +# endif +# ifndef HOST_l2c +# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff), \ + l) +# endif + +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + +# ifndef PEDANTIC +# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) +# if defined(__s390x__) +# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \ + :"=d"(l) :"m"(*(const unsigned int *)(c)));\ + (c)+=4; (l); }) +# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \ + :"=m"(*(unsigned int *)(c)) :"d"(l));\ + (c)+=4; (l); }) +# endif +# endif +# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) +# ifndef B_ENDIAN + /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */ +# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l) +# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l) +# endif +# endif +# endif + +# ifndef HOST_c2l +# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \ + l|=(((unsigned long)(*((c)++)))<< 8), \ + l|=(((unsigned long)(*((c)++)))<<16), \ + l|=(((unsigned long)(*((c)++)))<<24) ) +# endif +# ifndef HOST_l2c +# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24)&0xff), \ + l) +# endif + +#endif + +/* + * Time for some action:-) + */ + +int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len) +{ + const unsigned char *data = data_; + unsigned char *p; + HASH_LONG l; + size_t n; + + if (len == 0) + return 1; + + l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL; + /* + * 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai + * <weidai@eskimo.com> for pointing it out. + */ + if (l < c->Nl) /* overflow */ + c->Nh++; + c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on + * 16-bit */ + c->Nl = l; + + n = c->num; + if (n != 0) { + p = (unsigned char *)c->data; + + if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) { + memcpy(p + n, data, HASH_CBLOCK - n); + HASH_BLOCK_DATA_ORDER(c, p, 1); + n = HASH_CBLOCK - n; + data += n; + len -= n; + c->num = 0; + memset(p, 0, HASH_CBLOCK); /* keep it zeroed */ + } else { + memcpy(p + n, data, len); + c->num += (unsigned int)len; + return 1; + } + } + + n = len / HASH_CBLOCK; + if (n > 0) { + HASH_BLOCK_DATA_ORDER(c, data, n); + n *= HASH_CBLOCK; + data += n; + len -= n; + } + + if (len != 0) { + p = (unsigned char *)c->data; + c->num = (unsigned int)len; + memcpy(p, data, len); + } + return 1; +} + +void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data) +{ + HASH_BLOCK_DATA_ORDER(c, data, 1); +} + +int HASH_FINAL(unsigned char *md, HASH_CTX *c) +{ + unsigned char *p = (unsigned char *)c->data; + size_t n = c->num; + + p[n] = 0x80; /* there is always room for one */ + n++; + + if (n > (HASH_CBLOCK - 8)) { + memset(p + n, 0, HASH_CBLOCK - n); + n = 0; + HASH_BLOCK_DATA_ORDER(c, p, 1); + } + memset(p + n, 0, HASH_CBLOCK - 8 - n); + + p += HASH_CBLOCK - 8; +#if defined(DATA_ORDER_IS_BIG_ENDIAN) + (void)HOST_l2c(c->Nh, p); + (void)HOST_l2c(c->Nl, p); +#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN) + (void)HOST_l2c(c->Nl, p); + (void)HOST_l2c(c->Nh, p); +#endif + p -= HASH_CBLOCK; + HASH_BLOCK_DATA_ORDER(c, p, 1); + c->num = 0; + memset(p, 0, HASH_CBLOCK); + +#ifndef HASH_MAKE_STRING +# error "HASH_MAKE_STRING must be defined!" +#else + HASH_MAKE_STRING(c, md); +#endif + + return 1; +} + +#ifndef MD32_REG_T +# if defined(__alpha) || defined(__sparcv9) || defined(__mips) +# define MD32_REG_T long +/* + * This comment was originaly written for MD5, which is why it + * discusses A-D. But it basically applies to all 32-bit digests, + * which is why it was moved to common header file. + * + * In case you wonder why A-D are declared as long and not + * as MD5_LONG. Doing so results in slight performance + * boost on LP64 architectures. The catch is we don't + * really care if 32 MSBs of a 64-bit register get polluted + * with eventual overflows as we *save* only 32 LSBs in + * *either* case. Now declaring 'em long excuses the compiler + * from keeping 32 MSBs zeroed resulting in 13% performance + * improvement under SPARC Solaris7/64 and 5% under AlphaLinux. + * Well, to be honest it should say that this *prevents* + * performance degradation. + * <appro@fy.chalmers.se> + */ +# else +/* + * Above is not absolute and there are LP64 compilers that + * generate better code if MD32_REG_T is defined int. The above + * pre-processor condition reflects the circumstances under which + * the conclusion was made and is subject to further extension. + * <appro@fy.chalmers.se> + */ +# define MD32_REG_T int +# endif +#endif diff --git a/src/util/sha2.h b/src/util/sha2.h new file mode 100644 index 000000000..6ac045feb --- /dev/null +++ b/src/util/sha2.h @@ -0,0 +1,154 @@ +/* crypto/sha/sha.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_SHA_H +# define HEADER_SHA_H + +# include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +# if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1)) +# error SHA is disabled. +# endif + +# if defined(OPENSSL_FIPS) +# define FIPS_SHA_SIZE_T size_t +# endif + +/* + Compat stuff from OpenSSL land + */ + +/* crypto.h */ + +# define fips_md_init(alg) fips_md_init_ctx(alg, alg) + +# define fips_md_init_ctx(alg, cx) \ + int alg##_Init(cx##_CTX *c) +# define fips_cipher_abort(alg) while(0) + +/*- + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! SHA_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! SHA_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +# if defined(__LP32__) +# define SHA_LONG unsigned long +# elif defined(__ILP64__) +# define SHA_LONG unsigned long +# define SHA_LONG_LOG2 3 +# else +# define SHA_LONG unsigned int +# endif + +# define SHA_LBLOCK 16 +# define SHA_CBLOCK (SHA_LBLOCK*4)/* SHA treats input data as a + * contiguous array of 32 bit wide + * big-endian values. */ +# define SHA_LAST_BLOCK (SHA_CBLOCK-8) +# define SHA_DIGEST_LENGTH 20 + +typedef struct SHAstate_st { + SHA_LONG h0, h1, h2, h3, h4; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num; +} SHA_CTX; + +# define SHA256_CBLOCK (SHA_LBLOCK*4)/* SHA-256 treats input data as a + * contiguous array of 32 bit wide + * big-endian values. */ +# define SHA224_DIGEST_LENGTH 28 +# define SHA256_DIGEST_LENGTH 32 + +typedef struct SHA256state_st { + SHA_LONG h[8]; + SHA_LONG Nl, Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num, md_len; +} SHA256_CTX; + +# ifndef OPENSSL_NO_SHA256 +# ifdef OPENSSL_FIPS +int private_SHA224_Init(SHA256_CTX *c); +int private_SHA256_Init(SHA256_CTX *c); +# endif +int SHA224_Init(SHA256_CTX *c); +int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA224_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md); +int SHA256_Init(SHA256_CTX *c); +int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA256_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); +void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +# endif + +# define SHA384_DIGEST_LENGTH 48 +# define SHA512_DIGEST_LENGTH 64 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/util/sha256.c b/src/util/sha256.c new file mode 100644 index 000000000..311aac4a8 --- /dev/null +++ b/src/util/sha256.c @@ -0,0 +1,404 @@ +/* crypto/sha/sha256.c */ +/* ==================================================================== + * Copyright (c) 2004 The OpenSSL Project. All rights reserved + * according to the OpenSSL license [found in ../../LICENSE]. + * ==================================================================== + */ +# include <stdlib.h> +# include <string.h> + +# include <util/sha2.h> + +# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2a 19 Mar 2015" +# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT + +const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT; + +/* mem_clr.c */ +unsigned char cleanse_ctr = 0; +void OPENSSL_cleanse(void *ptr, size_t len) +{ + unsigned char *p = ptr; + size_t loop = len, ctr = cleanse_ctr; + while (loop--) { + *(p++) = (unsigned char)ctr; + ctr += (17 + ((size_t)p & 0xF)); + } + p = memchr(ptr, (unsigned char)ctr, len); + if (p) + ctr += (63 + (size_t)p); + cleanse_ctr = (unsigned char)ctr; +} + +# define fips_md_init(alg) fips_md_init_ctx(alg, alg) +# define fips_md_init_ctx(alg, cx) \ + int alg##_Init(cx##_CTX *c) +# define fips_cipher_abort(alg) while(0) + +fips_md_init_ctx(SHA224, SHA256) +{ + memset(c, 0, sizeof(*c)); + c->h[0] = 0xc1059ed8UL; + c->h[1] = 0x367cd507UL; + c->h[2] = 0x3070dd17UL; + c->h[3] = 0xf70e5939UL; + c->h[4] = 0xffc00b31UL; + c->h[5] = 0x68581511UL; + c->h[6] = 0x64f98fa7UL; + c->h[7] = 0xbefa4fa4UL; + c->md_len = SHA224_DIGEST_LENGTH; + return 1; +} + +fips_md_init(SHA256) +{ + memset(c, 0, sizeof(*c)); + c->h[0] = 0x6a09e667UL; + c->h[1] = 0xbb67ae85UL; + c->h[2] = 0x3c6ef372UL; + c->h[3] = 0xa54ff53aUL; + c->h[4] = 0x510e527fUL; + c->h[5] = 0x9b05688cUL; + c->h[6] = 0x1f83d9abUL; + c->h[7] = 0x5be0cd19UL; + c->md_len = SHA256_DIGEST_LENGTH; + return 1; +} + +unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md) +{ + SHA256_CTX c; + static unsigned char m[SHA224_DIGEST_LENGTH]; + + if (md == NULL) + md = m; + SHA224_Init(&c); + SHA256_Update(&c, d, n); + SHA256_Final(md, &c); + OPENSSL_cleanse(&c, sizeof(c)); + return (md); +} + +unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md) +{ + SHA256_CTX c; + static unsigned char m[SHA256_DIGEST_LENGTH]; + + if (md == NULL) + md = m; + SHA256_Init(&c); + SHA256_Update(&c, d, n); + SHA256_Final(md, &c); + OPENSSL_cleanse(&c, sizeof(c)); + return (md); +} + +int SHA224_Update(SHA256_CTX *c, const void *data, size_t len) +{ + return SHA256_Update(c, data, len); +} + +int SHA224_Final(unsigned char *md, SHA256_CTX *c) +{ + return SHA256_Final(md, c); +} + +# define DATA_ORDER_IS_BIG_ENDIAN + +# define HASH_LONG SHA_LONG +# define HASH_CTX SHA256_CTX +# define HASH_CBLOCK SHA_CBLOCK +/* + * Note that FIPS180-2 discusses "Truncation of the Hash Function Output." + * default: case below covers for it. It's not clear however if it's + * permitted to truncate to amount of bytes not divisible by 4. I bet not, + * but if it is, then default: case shall be extended. For reference. + * Idea behind separate cases for pre-defined lenghts is to let the + * compiler decide if it's appropriate to unroll small loops. + */ +# define HASH_MAKE_STRING(c,s) do { \ + unsigned long ll; \ + unsigned int nn; \ + switch ((c)->md_len) \ + { case SHA224_DIGEST_LENGTH: \ + for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++) \ + { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + case SHA256_DIGEST_LENGTH: \ + for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++) \ + { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + default: \ + if ((c)->md_len > SHA256_DIGEST_LENGTH) \ + return 0; \ + for (nn=0;nn<(c)->md_len/4;nn++) \ + { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \ + break; \ + } \ + } while (0) + +# define HASH_UPDATE SHA256_Update +# define HASH_TRANSFORM SHA256_Transform +# define HASH_FINAL SHA256_Final +# define HASH_BLOCK_DATA_ORDER sha256_block_data_order +# ifndef SHA256_ASM +static +# endif +void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num); + +# include "md32_common.h" + +# ifndef SHA256_ASM +static const SHA_LONG K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* + * FIPS specification refers to right rotations, while our ROTATE macro + * is left one. This is why you might notice that rotation coefficients + * differ from those observed in FIPS document by 32-N... + */ +# define Sigma0(x) (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10)) +# define Sigma1(x) (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7)) +# define sigma0(x) (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3)) +# define sigma1(x) (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10)) + +# define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +# define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +# ifdef OPENSSL_SMALL_FOOTPRINT + +static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, + size_t num) +{ + unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2; + SHA_LONG X[16], l; + int i; + const unsigned char *data = in; + + while (num--) { + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + f = ctx->h[5]; + g = ctx->h[6]; + h = ctx->h[7]; + + for (i = 0; i < 16; i++) { + HOST_c2l(data, l); + T1 = X[i] = l; + T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i]; + T2 = Sigma0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + for (; i < 64; i++) { + s0 = X[(i + 1) & 0x0f]; + s0 = sigma0(s0); + s1 = X[(i + 14) & 0x0f]; + s1 = sigma1(s1); + + T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf]; + T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i]; + T2 = Sigma0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + ctx->h[5] += f; + ctx->h[6] += g; + ctx->h[7] += h; + + } +} + +# else + +# define ROUND_00_15(i,a,b,c,d,e,f,g,h) do { \ + T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i]; \ + h = Sigma0(a) + Maj(a,b,c); \ + d += T1; h += T1; } while (0) + +# define ROUND_16_63(i,a,b,c,d,e,f,g,h,X) do { \ + s0 = X[(i+1)&0x0f]; s0 = sigma0(s0); \ + s1 = X[(i+14)&0x0f]; s1 = sigma1(s1); \ + T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f]; \ + ROUND_00_15(i,a,b,c,d,e,f,g,h); } while (0) + +static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, + size_t num) +{ + unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1; + SHA_LONG X[16]; + int i; + const unsigned char *data = in; + const union { + long one; + char little; + } is_endian = { + 1 + }; + + while (num--) { + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + f = ctx->h[5]; + g = ctx->h[6]; + h = ctx->h[7]; + + if (!is_endian.little && sizeof(SHA_LONG) == 4 + && ((size_t)in % 4) == 0) { + const SHA_LONG *W = (const SHA_LONG *)data; + + T1 = X[0] = W[0]; + ROUND_00_15(0, a, b, c, d, e, f, g, h); + T1 = X[1] = W[1]; + ROUND_00_15(1, h, a, b, c, d, e, f, g); + T1 = X[2] = W[2]; + ROUND_00_15(2, g, h, a, b, c, d, e, f); + T1 = X[3] = W[3]; + ROUND_00_15(3, f, g, h, a, b, c, d, e); + T1 = X[4] = W[4]; + ROUND_00_15(4, e, f, g, h, a, b, c, d); + T1 = X[5] = W[5]; + ROUND_00_15(5, d, e, f, g, h, a, b, c); + T1 = X[6] = W[6]; + ROUND_00_15(6, c, d, e, f, g, h, a, b); + T1 = X[7] = W[7]; + ROUND_00_15(7, b, c, d, e, f, g, h, a); + T1 = X[8] = W[8]; + ROUND_00_15(8, a, b, c, d, e, f, g, h); + T1 = X[9] = W[9]; + ROUND_00_15(9, h, a, b, c, d, e, f, g); + T1 = X[10] = W[10]; + ROUND_00_15(10, g, h, a, b, c, d, e, f); + T1 = X[11] = W[11]; + ROUND_00_15(11, f, g, h, a, b, c, d, e); + T1 = X[12] = W[12]; + ROUND_00_15(12, e, f, g, h, a, b, c, d); + T1 = X[13] = W[13]; + ROUND_00_15(13, d, e, f, g, h, a, b, c); + T1 = X[14] = W[14]; + ROUND_00_15(14, c, d, e, f, g, h, a, b); + T1 = X[15] = W[15]; + ROUND_00_15(15, b, c, d, e, f, g, h, a); + + data += SHA256_CBLOCK; + } else { + SHA_LONG l; + + HOST_c2l(data, l); + T1 = X[0] = l; + ROUND_00_15(0, a, b, c, d, e, f, g, h); + HOST_c2l(data, l); + T1 = X[1] = l; + ROUND_00_15(1, h, a, b, c, d, e, f, g); + HOST_c2l(data, l); + T1 = X[2] = l; + ROUND_00_15(2, g, h, a, b, c, d, e, f); + HOST_c2l(data, l); + T1 = X[3] = l; + ROUND_00_15(3, f, g, h, a, b, c, d, e); + HOST_c2l(data, l); + T1 = X[4] = l; + ROUND_00_15(4, e, f, g, h, a, b, c, d); + HOST_c2l(data, l); + T1 = X[5] = l; + ROUND_00_15(5, d, e, f, g, h, a, b, c); + HOST_c2l(data, l); + T1 = X[6] = l; + ROUND_00_15(6, c, d, e, f, g, h, a, b); + HOST_c2l(data, l); + T1 = X[7] = l; + ROUND_00_15(7, b, c, d, e, f, g, h, a); + HOST_c2l(data, l); + T1 = X[8] = l; + ROUND_00_15(8, a, b, c, d, e, f, g, h); + HOST_c2l(data, l); + T1 = X[9] = l; + ROUND_00_15(9, h, a, b, c, d, e, f, g); + HOST_c2l(data, l); + T1 = X[10] = l; + ROUND_00_15(10, g, h, a, b, c, d, e, f); + HOST_c2l(data, l); + T1 = X[11] = l; + ROUND_00_15(11, f, g, h, a, b, c, d, e); + HOST_c2l(data, l); + T1 = X[12] = l; + ROUND_00_15(12, e, f, g, h, a, b, c, d); + HOST_c2l(data, l); + T1 = X[13] = l; + ROUND_00_15(13, d, e, f, g, h, a, b, c); + HOST_c2l(data, l); + T1 = X[14] = l; + ROUND_00_15(14, c, d, e, f, g, h, a, b); + HOST_c2l(data, l); + T1 = X[15] = l; + ROUND_00_15(15, b, c, d, e, f, g, h, a); + } + + for (i = 16; i < 64; i += 8) { + ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X); + ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X); + ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X); + ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X); + ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X); + ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X); + ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X); + ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X); + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + ctx->h[5] += f; + ctx->h[6] += g; + ctx->h[7] += h; + + } +} + +# endif +# endif /* SHA256_ASM */ diff --git a/src/util/srp.cpp b/src/util/srp.cpp new file mode 100644 index 000000000..cc8bac6bb --- /dev/null +++ b/src/util/srp.cpp @@ -0,0 +1,1029 @@ +/* + * Secure Remote Password 6a implementation + * https://github.com/est31/csrp-gmp + * + * The MIT License (MIT) + * + * Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifdef WIN32 + #include <windows.h> + #include <wincrypt.h> +#else + #include <time.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <config.h> + +#if USE_SYSTEM_GMP || defined (__ANDROID__) || defined (ANDROID) + #include <gmp.h> +#else + #include <gmp/mini-gmp.h> +#endif + +#include <util/sha2.h> + +#include "srp.h" +//#define CSRP_USE_SHA1 +#define CSRP_USE_SHA256 + +#define srp_dbg_data(data, datalen, prevtext) ; +/*void srp_dbg_data(unsigned char * data, size_t datalen, char * prevtext) +{ + printf(prevtext); + size_t i; + for (i = 0; i < datalen; i++) + { + printf("%02X", data[i]); + } + printf("\n"); +}*/ + +static int g_initialized = 0; + +#define RAND_BUFF_MAX 128 +static unsigned int g_rand_idx; +static unsigned char g_rand_buff[RAND_BUFF_MAX]; + +typedef struct +{ + mpz_t N; + mpz_t g; +} NGConstant; + +struct NGHex +{ + const char* n_hex; + const char* g_hex; +}; + +/* All constants here were pulled from Appendix A of RFC 5054 */ +static struct NGHex global_Ng_constants[] = { + { /* 1024 */ + "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496" + "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E" + "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA" + "9AFD5138FE8376435B9FC61D2FC0EB06E3", + "2" + }, + { /* 2048 */ + "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4" + "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60" + "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF" + "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907" + "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861" + "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB" + "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73", + "2" + }, + { /* 4096 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF", + "5" + }, + { /* 8192 */ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", + "13" + }, + {0,0} /* null sentinel */ +}; + + +static NGConstant *new_ng( SRP_NGType ng_type, const char *n_hex, const char *g_hex ) +{ + NGConstant *ng = (NGConstant *) malloc(sizeof(NGConstant)); + mpz_init(ng->N); + mpz_init(ng->g); + + if (!ng || !ng->N || !ng->g) + return 0; + + if (ng_type != SRP_NG_CUSTOM) { + n_hex = global_Ng_constants[ ng_type ].n_hex; + g_hex = global_Ng_constants[ ng_type ].g_hex; + } + + mpz_set_str(ng->N, n_hex, 16); + mpz_set_str(ng->g, g_hex, 16); + + return ng; +} + +static void delete_ng( NGConstant *ng ) +{ + if (ng) { + mpz_clear(ng->N); + mpz_clear(ng->g); + free(ng); + } +} + + + +typedef union +{ + SHA_CTX sha; + SHA256_CTX sha256; + //SHA512_CTX sha512; +} HashCTX; + + +struct SRPVerifier +{ + SRP_HashAlgorithm hash_alg; + NGConstant *ng; + + char *username; + unsigned char *bytes_B; + int authenticated; + + unsigned char M[SHA512_DIGEST_LENGTH]; + unsigned char H_AMK[SHA512_DIGEST_LENGTH]; + unsigned char session_key[SHA512_DIGEST_LENGTH]; +}; + + +struct SRPUser +{ + SRP_HashAlgorithm hash_alg; + NGConstant *ng; + + mpz_t a; + mpz_t A; + mpz_t S; + + unsigned char *bytes_A; + int authenticated; + + char *username; + char *username_verifier; + unsigned char *password; + size_t password_len; + + unsigned char M[SHA512_DIGEST_LENGTH]; + unsigned char H_AMK[SHA512_DIGEST_LENGTH]; + unsigned char session_key[SHA512_DIGEST_LENGTH]; +}; + + +static int hash_init(SRP_HashAlgorithm alg, HashCTX *c) +{ + switch (alg) { +#ifdef CSRP_USE_SHA1 + case SRP_SHA1: return SHA1_Init(&c->sha); +#endif + /*case SRP_SHA224: return SHA224_Init(&c->sha256);*/ +#ifdef CSRP_USE_SHA256 + case SRP_SHA256: return SHA256_Init(&c->sha256); +#endif + /*case SRP_SHA384: return SHA384_Init(&c->sha512); + case SRP_SHA512: return SHA512_Init(&c->sha512);*/ + default: return -1; + }; +} +static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len ) +{ + switch (alg) { +#ifdef CSRP_USE_SHA1 + case SRP_SHA1: return SHA1_Update(&c->sha, data, len); +#endif + /*case SRP_SHA224: return SHA224_Update(&c->sha256, data, len);*/ +#ifdef CSRP_USE_SHA256 + case SRP_SHA256: return SHA256_Update(&c->sha256, data, len); +#endif + /*case SRP_SHA384: return SHA384_Update( &c->sha512, data, len ); + case SRP_SHA512: return SHA512_Update( &c->sha512, data, len );*/ + default: return -1; + }; +} +static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md ) +{ + switch (alg) { +#ifdef CSRP_USE_SHA1 + case SRP_SHA1: return SHA1_Final(md, &c->sha); +#endif + /*case SRP_SHA224: return SHA224_Final(md, &c->sha256);*/ +#ifdef CSRP_USE_SHA256 + case SRP_SHA256: return SHA256_Final(md, &c->sha256); +#endif + /*case SRP_SHA384: return SHA384_Final(md, &c->sha512); + case SRP_SHA512: return SHA512_Final(md, &c->sha512);*/ + default: return -1; + }; +} +static unsigned char *hash(SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md) +{ + switch (alg) { +#ifdef CSRP_USE_SHA1 + case SRP_SHA1: return SHA1(d, n, md); +#endif + /*case SRP_SHA224: return SHA224( d, n, md );*/ +#ifdef CSRP_USE_SHA256 + case SRP_SHA256: return SHA256(d, n, md); +#endif + /*case SRP_SHA384: return SHA384( d, n, md ); + case SRP_SHA512: return SHA512( d, n, md );*/ + default: return 0; + }; +} +static size_t hash_length(SRP_HashAlgorithm alg) +{ + switch (alg) { +#ifdef CSRP_USE_SHA1 + case SRP_SHA1: return SHA_DIGEST_LENGTH; +#endif + /*case SRP_SHA224: return SHA224_DIGEST_LENGTH;*/ +#ifdef CSRP_USE_SHA256 + case SRP_SHA256: return SHA256_DIGEST_LENGTH; +#endif + /*case SRP_SHA384: return SHA384_DIGEST_LENGTH; + case SRP_SHA512: return SHA512_DIGEST_LENGTH;*/ + default: return -1; + }; +} + +inline static int mpz_num_bytes(const mpz_t op) +{ + return (mpz_sizeinbase (op, 2) + 7) / 8; +} + +inline static void mpz_to_bin(const mpz_t op, unsigned char *to) +{ + mpz_export(to, NULL, 1, 1, 1, 0, op); +} + +inline static void mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret) +{ + mpz_import(ret, len, 1, 1, 1, 0, s); +} + +// set op to (op1 * op2) mod d, using tmp for the calculation +inline static void mpz_mulm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp) +{ + mpz_mul(tmp, op1, op2); + mpz_mod(op, tmp, d); +} + +// set op to (op1 + op2) mod d, using tmp for the calculation +inline static void mpz_addm( mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp ) +{ + mpz_add(tmp, op1, op2); + mpz_mod(op, tmp, d); +} + +// set op to (op1 - op2) mod d, using tmp for the calculation +inline static void mpz_subm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp) +{ + mpz_sub(tmp, op1, op2); + mpz_mod(op, tmp, d); +} + +static int H_nn(mpz_t result, SRP_HashAlgorithm alg, const mpz_t N, const mpz_t n1, const mpz_t n2) +{ + unsigned char buff[SHA512_DIGEST_LENGTH]; + size_t len_N = mpz_num_bytes(N); + size_t len_n1 = mpz_num_bytes(n1); + size_t len_n2 = mpz_num_bytes(n2); + size_t nbytes = len_N + len_N; + unsigned char *bin = (unsigned char *) malloc(nbytes); + if (!bin) + return 0; + if (len_n1 > len_N || len_n2 > len_N) { + free(bin); + return 0; + } + memset(bin, 0, nbytes); + mpz_to_bin(n1, bin + (len_N - len_n1)); + mpz_to_bin(n2, bin + (len_N + len_N - len_n2)); + hash( alg, bin, nbytes, buff ); + free(bin); + mpz_from_bin(buff, hash_length(alg), result); + return 1; +} + +static int H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n, size_t len_n, const unsigned char *bytes, size_t len_bytes) +{ + unsigned char buff[SHA512_DIGEST_LENGTH]; + size_t nbytes = len_n + len_bytes; + unsigned char *bin = (unsigned char *) malloc(nbytes); + if (!bin) + return 0; + memcpy(bin, n, len_n); + memcpy(bin + len_n, bytes, len_bytes); + hash(alg, bin, nbytes, buff); + free(bin); + mpz_from_bin(buff, hash_length(alg), result); + return 1; +} + +static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *salt, size_t salt_len, const char *username, const unsigned char *password, size_t password_len) +{ + unsigned char ucp_hash[SHA512_DIGEST_LENGTH]; + HashCTX ctx; + hash_init(alg, &ctx); + + srp_dbg_data((char*) username, strlen(username), "Username for x: "); + srp_dbg_data((char*) password, password_len, "Password for x: "); + hash_update(alg, &ctx, username, strlen(username)); + hash_update(alg, &ctx, ":", 1); + hash_update(alg, &ctx, password, password_len); + + hash_final(alg, &ctx, ucp_hash); + + return H_ns(result, alg, salt, salt_len, ucp_hash, hash_length(alg)); +} + +static void update_hash_n(SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n) +{ + size_t len = mpz_num_bytes(n); + unsigned char* n_bytes = (unsigned char *) malloc(len); + if (!n_bytes) + return; + mpz_to_bin(n, n_bytes); + hash_update(alg, ctx, n_bytes, len); + free(n_bytes); +} + +static void hash_num( SRP_HashAlgorithm alg, const mpz_t n, unsigned char *dest ) +{ + int nbytes = mpz_num_bytes(n); + unsigned char *bin = (unsigned char *) malloc(nbytes); + if(!bin) + return; + mpz_to_bin(n, bin); + hash(alg, bin, nbytes, dest); + free(bin); +} + +static void calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *dest, + const char *I, const unsigned char *s_bytes, size_t s_len, + const mpz_t A, const mpz_t B, const unsigned char *K) +{ + unsigned char H_N[SHA512_DIGEST_LENGTH]; + unsigned char H_g[SHA512_DIGEST_LENGTH]; + unsigned char H_I[SHA512_DIGEST_LENGTH]; + unsigned char H_xor[SHA512_DIGEST_LENGTH]; + HashCTX ctx; + size_t i = 0; + size_t hash_len = hash_length(alg); + + hash_num(alg, ng->N, H_N); + hash_num(alg, ng->g, H_g); + + hash(alg, (const unsigned char *)I, strlen(I), H_I); + + + for (i = 0; i < hash_len; i++ ) + H_xor[i] = H_N[i] ^ H_g[i]; + + hash_init(alg, &ctx); + + hash_update(alg, &ctx, H_xor, hash_len); + hash_update(alg, &ctx, H_I, hash_len); + hash_update(alg, &ctx, s_bytes, s_len); + update_hash_n(alg, &ctx, A); + update_hash_n(alg, &ctx, B); + hash_update(alg, &ctx, K, hash_len); + + hash_final(alg, &ctx, dest); +} + +static void calculate_H_AMK(SRP_HashAlgorithm alg, unsigned char *dest, const mpz_t A, const unsigned char *M, const unsigned char *K) +{ + HashCTX ctx; + + hash_init(alg, &ctx); + + update_hash_n(alg, &ctx, A); + hash_update(alg, &ctx, M, hash_length(alg)); + hash_update(alg, &ctx, K, hash_length(alg)); + + hash_final(alg, &ctx, dest); +} + + +struct srp_pcgrandom { + unsigned long long int m_state; + unsigned long long int m_inc; +}; typedef struct srp_pcgrandom srp_pcgrandom; + +static unsigned long int srp_pcgrandom_next(srp_pcgrandom *r) +{ + unsigned long long int oldstate = r->m_state; + r->m_state = oldstate * 6364136223846793005ULL + r->m_inc; + + unsigned long int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + unsigned long int rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + +static void srp_pcgrandom_seed(srp_pcgrandom *r, unsigned long long int state, + unsigned long long int seq) +{ + r->m_state = 0U; + r->m_inc = (seq << 1u) | 1u; + srp_pcgrandom_next(r); + r->m_state += state; + srp_pcgrandom_next(r); +} + + +static int fill_buff() +{ + g_rand_idx = 0; + +#ifdef WIN32 + HCRYPTPROV wctx; +#else + FILE *fp = 0; +#endif + +#ifdef WIN32 + + CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE*) g_rand_buff); + CryptReleaseContext(wctx, 0); + + return 1; + +#else + fp = fopen("/dev/urandom", "r"); + + if (fp) { + fread(g_rand_buff, sizeof(g_rand_buff), 1, fp); + fclose(fp); + } else { + srp_pcgrandom *r = (srp_pcgrandom *) malloc(sizeof(srp_pcgrandom)); + srp_pcgrandom_seed(r, time(NULL) ^ clock(), 0xda3e39cb94b95bdbULL); + size_t i = 0; + for (i = 0; i < RAND_BUFF_MAX; i++) { + g_rand_buff[i] = srp_pcgrandom_next(r); + } + } +#endif + return 1; +} + +static void mpz_fill_random(mpz_t num) +{ + // was call: BN_rand(num, 256, -1, 0); + if (RAND_BUFF_MAX - g_rand_idx < 32) + fill_buff(); + mpz_from_bin((const unsigned char *) (&g_rand_buff[g_rand_idx]), 32, num); + g_rand_idx += 32; +} + +static void init_random() +{ + if (g_initialized) + return; + g_initialized = fill_buff(); +} + +#define srp_dbg_num(num, text) ; +/*void srp_dbg_num(mpz_t num, char * prevtext) +{ + int len_num = mpz_num_bytes(num); + char *bytes_num = (char*) malloc(len_num); + mpz_to_bin(num, (unsigned char *) bytes_num); + srp_dbg_data(bytes_num, len_num, prevtext); + free(bytes_num); + +}*/ + +/*********************************************************************************************************** + * + * Exported Functions + * + ***********************************************************************************************************/ + +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char *username_for_verifier, + const unsigned char *password, size_t len_password, + unsigned char **bytes_s, size_t *len_s, + unsigned char **bytes_v, size_t *len_v, + const char *n_hex, const char *g_hex ) +{ + mpz_t v; mpz_init(v); + mpz_t x; mpz_init(x); + NGConstant *ng = new_ng(ng_type, n_hex, g_hex); + + if(!ng) + goto cleanup_and_exit; + + if (*bytes_s == NULL) { + *len_s = 16; + if (RAND_BUFF_MAX - g_rand_idx < 16) + fill_buff(); + *bytes_s = (unsigned char*)malloc(sizeof(char) * 16); + memcpy(*bytes_s, &g_rand_buff + g_rand_idx, sizeof(char) * 16); + g_rand_idx += 16; + } + + + if (!calculate_x(x, alg, *bytes_s, *len_s, username_for_verifier, + password, len_password)) + goto cleanup_and_exit; + + srp_dbg_num(x, "Server calculated x: "); + + mpz_powm(v, ng->g, x, ng->N); + + *len_v = mpz_num_bytes(v); + + *bytes_v = (unsigned char*)malloc(*len_v); + + if (!bytes_v) + goto cleanup_and_exit; + + mpz_to_bin(v, *bytes_v); + +cleanup_and_exit: + delete_ng( ng ); + mpz_clear(v); + mpz_clear(x); +} + + + +/* Out: bytes_B, len_B. + * + * On failure, bytes_B will be set to NULL and len_B will be set to 0 + */ +struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char *username, + const unsigned char *bytes_s, size_t len_s, + const unsigned char *bytes_v, size_t len_v, + const unsigned char *bytes_A, size_t len_A, + const unsigned char *bytes_b, size_t len_b, + unsigned char **bytes_B, size_t *len_B, + const char *n_hex, const char *g_hex ) +{ + mpz_t v; mpz_init(v); mpz_from_bin(bytes_v, len_v, v); + mpz_t A; mpz_init(A); mpz_from_bin(bytes_A, len_A, A); + mpz_t u; mpz_init(u); + mpz_t B; mpz_init(B); + mpz_t S; mpz_init(S); + mpz_t b; mpz_init(b); + mpz_t k; mpz_init(k); + mpz_t tmp1; mpz_init(tmp1); + mpz_t tmp2; mpz_init(tmp2); + mpz_t tmp3; mpz_init(tmp3); + size_t ulen = strlen(username) + 1; + NGConstant *ng = new_ng(ng_type, n_hex, g_hex); + struct SRPVerifier *ver = 0; + + *len_B = 0; + *bytes_B = 0; + + if (!ng) + goto cleanup_and_exit; + + ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) ); + + if (!ver) + goto cleanup_and_exit; + + init_random(); /* Only happens once */ + + ver->username = (char *) malloc(ulen); + ver->hash_alg = alg; + ver->ng = ng; + + if (!ver->username) { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + memcpy((char*)ver->username, username, ulen); + + ver->authenticated = 0; + + /* SRP-6a safety check */ + mpz_mod(tmp1, A, ng->N); + if (mpz_sgn(tmp1) != 0) { + if (bytes_b) { + mpz_from_bin(bytes_b, len_b, b); + } else { + mpz_fill_random(b); + } + + if (!H_nn(k, alg, ng->N, ng->N, ng->g)) { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + /* B = kv + g^b */ + mpz_mulm(tmp1, k, v, ng->N, tmp3); + mpz_powm(tmp2, ng->g, b, ng->N); + mpz_addm(B, tmp1, tmp2, ng->N, tmp3); + + if (!H_nn(u, alg, ng->N, A, B)) { + free(ver); + ver = 0; + goto cleanup_and_exit; + } + + srp_dbg_num(u, "Server calculated u: "); + + /* S = (A *(v^u)) ^ b */ + mpz_powm(tmp1, v, u, ng->N); + mpz_mulm(tmp2, A, tmp1, ng->N, tmp3); + mpz_powm(S, tmp2, b, ng->N); + + hash_num(alg, S, ver->session_key); + + calculate_M(alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key); + calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key); + + *len_B = mpz_num_bytes(B); + *bytes_B = (unsigned char*)malloc(*len_B); + + if (!*bytes_B) { + free(ver->username); + free(ver); + ver = 0; + *len_B = 0; + goto cleanup_and_exit; + } + + mpz_to_bin(B, *bytes_B); + + ver->bytes_B = *bytes_B; + } else { + free(ver); + ver = 0; + } + +cleanup_and_exit: + mpz_clear(v); + mpz_clear(A); + mpz_clear(u); + mpz_clear(k); + mpz_clear(B); + mpz_clear(S); + mpz_clear(b); + mpz_clear(tmp1); + mpz_clear(tmp2); + mpz_clear(tmp3); + return ver; +} + + + + +void srp_verifier_delete(struct SRPVerifier *ver) +{ + if (ver) { + delete_ng(ver->ng); + free(ver->username); + free(ver->bytes_B); + memset(ver, 0, sizeof(*ver)); + free(ver); + } +} + + + +int srp_verifier_is_authenticated(struct SRPVerifier *ver) +{ + return ver->authenticated; +} + + +const char *srp_verifier_get_username(struct SRPVerifier *ver) +{ + return ver->username; +} + + +const unsigned char *srp_verifier_get_session_key(struct SRPVerifier *ver, size_t *key_length) +{ + if (key_length) + *key_length = hash_length(ver->hash_alg); + return ver->session_key; +} + + +size_t srp_verifier_get_session_key_length(struct SRPVerifier *ver) +{ + return hash_length(ver->hash_alg); +} + + +/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */ +void srp_verifier_verify_session(struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK) +{ + if (memcmp(ver->M, user_M, hash_length(ver->hash_alg)) == 0) { + ver->authenticated = 1; + *bytes_HAMK = ver->H_AMK; + } else + *bytes_HAMK = NULL; +} + +/*******************************************************************************/ + +struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, + const char *username, const char *username_for_verifier, + const unsigned char *bytes_password, size_t len_password, + const char *n_hex, const char *g_hex) +{ + struct SRPUser *usr = (struct SRPUser *) malloc(sizeof(struct SRPUser)); + size_t ulen = strlen(username) + 1; + size_t uvlen = strlen(username_for_verifier) + 1; + + if (!usr) + goto err_exit; + + init_random(); /* Only happens once */ + + usr->hash_alg = alg; + usr->ng = new_ng(ng_type, n_hex, g_hex); + + mpz_init(usr->a); + mpz_init(usr->A); + mpz_init(usr->S); + + if (!usr->ng || !usr->a || !usr->A || !usr->S) + goto err_exit; + + usr->username = (char*)malloc(ulen); + usr->username_verifier = (char*)malloc(uvlen); + usr->password = (unsigned char*)malloc(len_password); + usr->password_len = len_password; + + if (!usr->username || !usr->password) + goto err_exit; + + memcpy(usr->username, username, ulen); + memcpy(usr->username_verifier, username_for_verifier, uvlen); + memcpy(usr->password, bytes_password, len_password); + + usr->authenticated = 0; + + usr->bytes_A = 0; + + return usr; + +err_exit: + if (usr) { + mpz_clear(usr->a); + mpz_clear(usr->A); + mpz_clear(usr->S); + if (usr->username) + free(usr->username); + if (usr->username_verifier) + free(usr->username_verifier); + if (usr->password) { + memset(usr->password, 0, usr->password_len); + free(usr->password); + } + free(usr); + } + + return 0; +} + + + +void srp_user_delete(struct SRPUser *usr) +{ + if(usr) { + mpz_clear(usr->a); + mpz_clear(usr->A); + mpz_clear(usr->S); + + delete_ng(usr->ng); + + memset(usr->password, 0, usr->password_len); + + free(usr->username); + free(usr->username_verifier); + free(usr->password); + + if (usr->bytes_A) + free(usr->bytes_A); + + memset(usr, 0, sizeof(*usr)); + free(usr); + } +} + + + +int srp_user_is_authenticated(struct SRPUser *usr) +{ + return usr->authenticated; +} + + +const char *srp_user_get_username(struct SRPUser *usr) +{ + return usr->username; +} + + +const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length) +{ + if (key_length) + *key_length = hash_length(usr->hash_alg); + return usr->session_key; +} + + +size_t srp_user_get_session_key_length(struct SRPUser *usr) +{ + return hash_length(usr->hash_alg); +} + + +/* Output: username, bytes_A, len_A */ +void srp_user_start_authentication(struct SRPUser *usr, char **username, + const unsigned char *bytes_a, size_t len_a, + unsigned char **bytes_A, size_t *len_A) +{ + if (bytes_a) { + mpz_from_bin(bytes_a, len_a, usr->a); + } else { + mpz_fill_random(usr->a); + } + + mpz_powm(usr->A, usr->ng->g, usr->a, usr->ng->N); + + *len_A = mpz_num_bytes(usr->A); + *bytes_A = (unsigned char*)malloc(*len_A); + + if (!*bytes_A) { + *len_A = 0; + *bytes_A = 0; + *username = 0; + return; + } + + mpz_to_bin(usr->A, *bytes_A); + + usr->bytes_A = *bytes_A; + if (username) + *username = usr->username; +} + + +/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */ +void srp_user_process_challenge(struct SRPUser *usr, + const unsigned char *bytes_s, size_t len_s, + const unsigned char *bytes_B, size_t len_B, + unsigned char **bytes_M, size_t *len_M) +{ + mpz_t B; mpz_init(B); mpz_from_bin(bytes_B, len_B, B); + mpz_t u; mpz_init(u); + mpz_t x; mpz_init(x); + mpz_t k; mpz_init(k); + mpz_t v; mpz_init(v); + mpz_t tmp1; mpz_init(tmp1); + mpz_t tmp2; mpz_init(tmp2); + mpz_t tmp3; mpz_init(tmp3); + mpz_t tmp4; mpz_init(tmp4); + + *len_M = 0; + *bytes_M = 0; + + if (!H_nn(u, usr->hash_alg, usr->ng->N, usr->A, B)) + goto cleanup_and_exit; + + srp_dbg_num(u, "Client calculated u: "); + + if (!calculate_x(x, usr->hash_alg, bytes_s, len_s, + usr->username_verifier, usr->password, usr->password_len)) + goto cleanup_and_exit; + + srp_dbg_num(x, "Client calculated x: "); + + if (!H_nn(k, usr->hash_alg, usr->ng->N, usr->ng->N, usr->ng->g)) + goto cleanup_and_exit; + + /* SRP-6a safety check */ + if ( mpz_sgn(B) != 0 && mpz_sgn(u) != 0 ) { + mpz_powm(v, usr->ng->g, x, usr->ng->N); + + srp_dbg_num(v, "Client calculated v: "); + + /* S = (B - k*(g^x)) ^ (a + ux) */ + mpz_mul(tmp1, u, x); + mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */ + mpz_powm(tmp1, usr->ng->g, x, usr->ng->N); /* tmp1 = g^x */ + mpz_mulm(tmp3, k, tmp1, usr->ng->N, tmp4); /* tmp3 = k*(g^x) */ + mpz_subm(tmp1, B, tmp3, usr->ng->N, tmp4); /* tmp1 = (B - K*(g^x)) */ + mpz_powm(usr->S, tmp1, tmp2, usr->ng->N); + + hash_num(usr->hash_alg, usr->S, usr->session_key); + + calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_s, usr->A,B, usr->session_key ); + calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key ); + + *bytes_M = usr->M; + if (len_M) + *len_M = hash_length( usr->hash_alg ); + } else { + *bytes_M = NULL; + if (len_M) + *len_M = 0; + } + +cleanup_and_exit: + + mpz_clear(B); + mpz_clear(u); + mpz_clear(x); + mpz_clear(k); + mpz_clear(v); + mpz_clear(tmp1); + mpz_clear(tmp2); + mpz_clear(tmp3); + mpz_clear(tmp4); +} + + +void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK) +{ + if (memcmp(usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg)) == 0) + usr->authenticated = 1; +} diff --git a/src/util/srp.h b/src/util/srp.h new file mode 100644 index 000000000..15a2b8a68 --- /dev/null +++ b/src/util/srp.h @@ -0,0 +1,171 @@ +/* + * Secure Remote Password 6a implementation + * https://github.com/est31/csrp-gmp + * + * The MIT License (MIT) + * + * Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * + * Purpose: This is a direct implementation of the Secure Remote Password + * Protocol version 6a as described by + * http://srp.stanford.edu/design.html + * + * Author: tom.cocagne@gmail.com (Tom Cocagne) + * + * Dependencies: LibGMP + * + * Usage: Refer to test_srp.c for a demonstration + * + * Notes: + * This library allows multiple combinations of hashing algorithms and + * prime number constants. For authentication to succeed, the hash and + * prime number constants must match between + * srp_create_salted_verification_key(), srp_user_new(), + * and srp_verifier_new(). A recommended approach is to determine the + * desired level of security for an application and globally define the + * hash and prime number constants to the predetermined values. + * + * As one might suspect, more bits means more security. As one might also + * suspect, more bits also means more processing time. The test_srp.c + * program can be easily modified to profile various combinations of + * hash & prime number pairings. + */ + +#ifndef SRP_H +#define SRP_H + + +struct SRPVerifier; +struct SRPUser; + +typedef enum +{ + SRP_NG_1024, + SRP_NG_2048, + SRP_NG_4096, + SRP_NG_8192, + SRP_NG_CUSTOM +} SRP_NGType; + +typedef enum +{ + /*SRP_SHA1,*/ + /*SRP_SHA224,*/ + SRP_SHA256, + /*SRP_SHA384, + SRP_SHA512*/ +} SRP_HashAlgorithm; + +/* Out: bytes_v, len_v + * + * The caller is responsible for freeing the memory allocated for bytes_v + * + * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type. + * If provided, they must contain ASCII text of the hexidecimal notation. + * + * If bytes_s == NULL, it is filled with random data. The caller is responsible for freeing. + */ +void srp_create_salted_verification_key( SRP_HashAlgorithm alg, + SRP_NGType ng_type, const char *username_for_verifier, + const unsigned char *password, size_t len_password, + unsigned char **bytes_s, size_t *len_s, + unsigned char **bytes_v, size_t *len_v, + const char * n_hex, const char *g_hex ); + +/* Out: bytes_B, len_B. + * + * On failure, bytes_B will be set to NULL and len_B will be set to 0 + * + * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type + * + * If bytes_b == NULL, random data is used for b. + */ +struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, + const char *username, + const unsigned char *bytes_s, size_t len_s, + const unsigned char *bytes_v, size_t len_v, + const unsigned char *bytes_A, size_t len_A, + const unsigned char *bytes_b, size_t len_b, + unsigned char** bytes_B, size_t *len_B, + const char* n_hex, const char* g_hex); + + +void srp_verifier_delete( struct SRPVerifier* ver ); + + +int srp_verifier_is_authenticated( struct SRPVerifier* ver ); + + +const char * srp_verifier_get_username( struct SRPVerifier* ver ); + +/* key_length may be null */ +const unsigned char* srp_verifier_get_session_key( struct SRPVerifier* ver, + size_t *key_length ); + + +size_t srp_verifier_get_session_key_length(struct SRPVerifier* ver); + + +/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */ +void srp_verifier_verify_session( struct SRPVerifier* ver, + const unsigned char* user_M, unsigned char** bytes_HAMK ); + +/*******************************************************************************/ + +/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */ +struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, + const char *username, const char *username_for_verifier, + const unsigned char *bytes_password, size_t len_password, + const char *n_hex, const char *g_hex); + +void srp_user_delete(struct SRPUser * usr); + +int srp_user_is_authenticated(struct SRPUser * usr); + + +const char* srp_user_get_username(struct SRPUser * usr); + +/* key_length may be null */ +const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length); + +size_t srp_user_get_session_key_length(struct SRPUser* usr); + +/* Output: username, bytes_A, len_A. If you don't want it get written, set username to NULL. + * If bytes_a == NULL, random data is used for a. */ +void srp_user_start_authentication(struct SRPUser* usr, char** username, + const unsigned char* bytes_a, size_t len_a, + unsigned char** bytes_A, size_t* len_A); + +/* Output: bytes_M, len_M (len_M may be null and will always be + * srp_user_get_session_key_length() bytes in size) */ +void srp_user_process_challenge(struct SRPUser *usr, + const unsigned char *bytes_s, size_t len_s, + const unsigned char *bytes_B, size_t len_B, + unsigned char **bytes_M, size_t *len_M); + +/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */ +void srp_user_verify_session(struct SRPUser* usr, const unsigned char* bytes_HAMK); + +#endif /* Include Guard */ diff --git a/src/util/string.cpp b/src/util/string.cpp index c0f0e97fd..231eaf6be 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -22,12 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "numeric.h" #include "log.h" -#include "sha1.h" -#include "base64.h" #include "hex.h" #include "../porting.h" -#include <algorithm> #include <sstream> #include <iomanip> #include <map> @@ -176,26 +173,6 @@ std::string wide_to_narrow(const std::wstring &wcs) #endif -// Get an sha-1 hash of the player's name combined with -// the password entered. That's what the server uses as -// their password. (Exception : if the password field is -// blank, we send a blank password - this is for backwards -// compatibility with password-less players). -std::string translatePassword(const std::string &playername, - const std::string &password) -{ - if (password.length() == 0) - return ""; - - std::string slt = playername + password; - SHA1 sha1; - sha1.addBytes(slt.c_str(), slt.length()); - unsigned char *digest = sha1.getDigest(); - std::string pwd = base64_encode(digest, 20); - free(digest); - return pwd; -} - std::string urlencode(std::string str) { // Encodes non-unreserved URI characters by a percent sign diff --git a/src/util/string.h b/src/util/string.h index f2d9af570..4ab5cf3f7 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -43,8 +43,7 @@ wchar_t *narrow_to_wide_c(const char *str); std::wstring narrow_to_wide(const std::string &mbs); std::string wide_to_narrow(const std::wstring &wcs); -std::string translatePassword(const std::string &playername, - const std::string &password); + std::string urlencode(std::string str); std::string urldecode(std::string str); u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask); |