diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index 8f25e936..dc7c3f78 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -6,7 +6,8 @@ namespace Components Auth::TokenIncrementing Auth::TokenContainer; Utils::Cryptography::Token Auth::GuidToken; - Utils::Cryptography::ECDSA::Key Auth::GuidKey; + Utils::Cryptography::Token Auth::ComputeToken; + Utils::Cryptography::ECC::Key Auth::GuidKey; void Auth::Frame() { @@ -146,7 +147,7 @@ namespace Components { Auth::LoadKey(); std::string key = Auth::GuidKey.GetPublicKey(); - return (Utils::OneAtATime(key.data(), key.size())); + return (Utils::Cryptography::JenkinsOneAtATime::Compute(key.data(), key.size())); } void Auth::StoreKey() @@ -155,6 +156,7 @@ namespace Components { Proto::Auth::Certificate cert; cert.set_token(Auth::GuidToken.ToString()); + cert.set_ctoken(Auth::ComputeToken.ToString()); cert.set_privatekey(Auth::GuidKey.Export(PK_PRIVATE)); Utils::WriteFile("players/guid.dat", cert.SerializeAsString()); @@ -171,6 +173,7 @@ namespace Components { Auth::GuidKey.Import(cert.privatekey(), PK_PRIVATE); Auth::GuidToken = cert.token(); + Auth::ComputeToken = cert.ctoken(); } else { @@ -180,7 +183,8 @@ namespace Components if (!Auth::GuidKey.IsValid()) { Auth::GuidToken.Clear(); - Auth::GuidKey = Utils::Cryptography::ECDSA::GenerateKey(512); + Auth::ComputeToken.Clear(); + Auth::GuidKey = Utils::Cryptography::ECC::GenerateKey(512); Auth::StoreKey(); } } @@ -209,7 +213,7 @@ namespace Components Auth::TokenContainer.generating = true; Auth::TokenContainer.hashes = 0; Auth::TokenContainer.startTime = Game::Com_Milliseconds(); - Auth::IncrementToken(Auth::GuidToken, Auth::GuidKey.GetPublicKey(), Auth::TokenContainer.targetLevel, &Auth::TokenContainer.cancel, &Auth::TokenContainer.hashes); + Auth::IncrementToken(Auth::GuidToken, Auth::ComputeToken, Auth::GuidKey.GetPublicKey(), Auth::TokenContainer.targetLevel, &Auth::TokenContainer.cancel, &Auth::TokenContainer.hashes); Auth::TokenContainer.generating = false; if (Auth::TokenContainer.cancel) @@ -250,27 +254,30 @@ namespace Components return bits; } - void Auth::IncrementToken(Utils::Cryptography::Token& token, std::string publicKey, uint32_t zeroBits, bool* cancel, uint64_t* count) + void Auth::IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel, uint64_t* count) { if (zeroBits > 512) return; // Not possible, due to SHA512 - Utils::Cryptography::Token tempToken(token); + if (computeToken < token) + { + computeToken = token; + } // Check if we already have the desired security level - uint32_t lastLevel = Auth::GetZeroBits(tempToken, publicKey); + uint32_t lastLevel = Auth::GetZeroBits(token, publicKey); uint32_t level = lastLevel; if (level >= zeroBits) return; do { - ++tempToken; + ++computeToken; if (count) ++(*count); - level = Auth::GetZeroBits(tempToken, publicKey); + level = Auth::GetZeroBits(computeToken, publicKey); // Store level if higher than the last one if (level >= lastLevel) { - token = tempToken; + token = computeToken; lastLevel = level; } @@ -279,7 +286,7 @@ namespace Components } while (level < zeroBits); - token = tempToken; + token = computeToken; } Auth::Auth() @@ -309,7 +316,7 @@ namespace Components Proto::Auth::Response response; response.set_token(Auth::GuidToken.ToString()); response.set_publickey(Auth::GuidKey.GetPublicKey()); - response.set_signature(Utils::Cryptography::ECDSA::SignMessage(Auth::GuidKey, data)); + response.set_signature(Utils::Cryptography::ECC::SignMessage(Auth::GuidKey, data)); Network::SendCommand(address, "xuidAuthResp", response.SerializeAsString()); }); @@ -337,7 +344,7 @@ namespace Components } // Check if guid matches the certificate - else if (id != (Utils::OneAtATime(response.publickey().data(), response.publickey().size()) & ~0x80000000)) + else if (id != (Utils::Cryptography::JenkinsOneAtATime::Compute(response.publickey().data(), response.publickey().size()) & ~0x80000000)) { info->state = Auth::STATE_INVALID; Game::SV_KickClientError(client, "XUID doesn't match the certificate!"); @@ -348,7 +355,7 @@ namespace Components { info->publicKey.Set(response.publickey()); - if (Utils::Cryptography::ECDSA::VerifyMessage(info->publicKey, info->challenge, response.signature())) + if (Utils::Cryptography::ECC::VerifyMessage(info->publicKey, info->challenge, response.signature())) { uint32_t ourLevel = static_cast(Dvar::Var("sv_securityLevel").Get()); uint32_t userLevel = Auth::GetZeroBits(response.token(), response.publickey()); @@ -399,6 +406,7 @@ namespace Components { Logger::Print("Your current security level is %d\n", Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.GetPublicKey())); Logger::Print("Your security token is: %s\n", Utils::DumpHex(Auth::GuidToken.ToString(), "").data()); + Logger::Print("Your computation token is: %s\n", Utils::DumpHex(Auth::ComputeToken.ToString(), "").data()); } else { @@ -433,4 +441,65 @@ namespace Components Auth::StoreKey(); } + + bool Auth::UnitTest() + { + bool success = true; + + printf("Testing logical token operators:\n"); + + Utils::Cryptography::Token token1; + Utils::Cryptography::Token token2; + ++token1, token2++; // Test incrementation operator + + printf("Operator == : "); + if (token1 == token2 && !(++token1 == token2)) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator != : "); + if (token1 != token2 && !(++token2 != token1)) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator >= : "); + if (token1 >= token2 && ++token1 >= token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator > : "); + if (token1 > token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator <= : "); + if (token1 <= ++token2 && token1 <= ++token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator < : "); + if (token1 < token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + return success; + } } diff --git a/src/Components/Modules/Auth.hpp b/src/Components/Modules/Auth.hpp index 6db9a6a9..77bb3385 100644 --- a/src/Components/Modules/Auth.hpp +++ b/src/Components/Modules/Auth.hpp @@ -6,6 +6,7 @@ namespace Components Auth(); ~Auth(); const char* GetName() { return "Auth"; }; + bool UnitTest(); static void StoreKey(); static void LoadKey(bool force = false); @@ -15,7 +16,7 @@ namespace Components static void IncreaseSecurityLevel(uint32_t level, std::string command = ""); static uint32_t GetZeroBits(Utils::Cryptography::Token token, std::string publicKey); - static void IncrementToken(Utils::Cryptography::Token& token, std::string publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr); + static void IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr); private: @@ -29,7 +30,7 @@ namespace Components struct AuthInfo { - Utils::Cryptography::ECDSA::Key publicKey; + Utils::Cryptography::ECC::Key publicKey; std::string challenge; AuthState state; int time; @@ -50,7 +51,8 @@ namespace Components static TokenIncrementing TokenContainer; static Utils::Cryptography::Token GuidToken; - static Utils::Cryptography::ECDSA::Key GuidKey; + static Utils::Cryptography::Token ComputeToken; + static Utils::Cryptography::ECC::Key GuidKey; static void Frame(); diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index ec8c47e6..3154cd17 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -145,7 +145,7 @@ namespace Components download->lastPing = Game::Com_Milliseconds(); std::string packetData(data.data() + sizeof(Download::Container::Packet), packet->length); - if (packet->hash == Utils::OneAtATime(packetData.data(), packetData.size())) + if (packet->hash == Utils::Cryptography::JenkinsOneAtATime::Compute(packetData.data(), packetData.size())) { //Logger::Print("Packet added!\n"); download->parts[packet->partId] = packetData; @@ -302,7 +302,7 @@ namespace Components std::string data(download->buffer.data() + (packet * PACKET_SIZE), size); packetContainer.length = data.size(); - packetContainer.hash = Utils::OneAtATime(data.data(), data.size()); + packetContainer.hash = Utils::Cryptography::JenkinsOneAtATime::Compute(data.data(), data.size()); std::string response = "dlPacketResponse\n"; response.append(reinterpret_cast(&packetContainer), sizeof(packetContainer)); diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index 32949640..d2d2918b 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -140,7 +140,7 @@ namespace Components void Network::SendCommand(Game::netsrc_t type, Network::Address target, std::string command, std::string data) { - // Use space a separator (possible separators are '\n', ' '). + // Use space as separator (possible separators are '\n', ' '). // Though, our handler only needs exactly 1 char as separator and doesn't which char it is std::string packet; packet.append(command); diff --git a/src/Components/Modules/Node.cpp b/src/Components/Modules/Node.cpp index f3eb0dc5..3cdf3fd9 100644 --- a/src/Components/Modules/Node.cpp +++ b/src/Components/Modules/Node.cpp @@ -2,7 +2,7 @@ namespace Components { - Utils::Cryptography::ECDSA::Key Node::SignatureKey; + Utils::Cryptography::ECC::Key Node::SignatureKey; std::vector Node::Nodes; std::vector Node::Sessions; @@ -346,7 +346,7 @@ namespace Components if (ZoneBuilder::IsEnabled()) return; // Generate our ECDSA key - Node::SignatureKey = Utils::Cryptography::ECDSA::GenerateKey(512); + Node::SignatureKey = Utils::Cryptography::ECC::GenerateKey(512); // Load stored nodes Dvar::OnInit([] () @@ -364,7 +364,7 @@ namespace Components Proto::Node::Packet packet; packet.set_challenge(challenge); - packet.set_signature(Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, challenge)); + packet.set_signature(Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, challenge)); for (auto node : Node::Nodes) { @@ -394,7 +394,7 @@ namespace Components if (!packet.ParseFromString(data)) return; if (packet.challenge().empty()) return; - std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, packet.challenge()); + std::string signature = Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, packet.challenge()); std::string challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt()); // The challenge this client sent is exactly the challenge we stored for this client @@ -441,7 +441,7 @@ namespace Components // Verify signature entry->publicKey.Set(publicKey); - if (!Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, entry->challenge, signature)) + if (!Utils::Cryptography::ECC::VerifyMessage(entry->publicKey, entry->challenge, signature)) { Logger::Print("Signature from %s for challenge '%s' is invalid!\n", address.GetString(), entry->challenge.data()); return; @@ -459,7 +459,7 @@ namespace Components // Build response publicKey = Node::SignatureKey.GetPublicKey(); - signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, challenge); + signature = Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, challenge); packet.Clear(); packet.set_signature(signature); @@ -488,7 +488,7 @@ namespace Components entry->publicKey.Set(publicKey); - if (Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, entry->challenge, signature)) + if (Utils::Cryptography::ECC::VerifyMessage(entry->publicKey, entry->challenge, signature)) { entry->lastTime = Game::Com_Milliseconds(); entry->state = Node::STATE_VALID; @@ -552,7 +552,7 @@ namespace Components std::string challenge = packet.challenge(); std::string signature = packet.signature(); - if (Utils::Cryptography::ECDSA::VerifyMessage(entry->publicKey, challenge, signature)) + if (Utils::Cryptography::ECC::VerifyMessage(entry->publicKey, challenge, signature)) { entry->lastHeard = Game::Com_Milliseconds(); entry->lastTime = Game::Com_Milliseconds(); @@ -624,7 +624,7 @@ namespace Components if (!entry) return; #ifdef DEBUG - Logger::Print("Session initialization received. Synchronizing...\n", address.GetString()); + Logger::Print("Session initialization received from %s. Synchronizing...\n", address.GetString()); #endif entry->lastTime = Game::Com_Milliseconds(); @@ -641,7 +641,7 @@ namespace Components entry->lastTime = Game::Com_Milliseconds(); #ifdef DEBUG - Logger::Print("Session acknowledged, synchronizing node list...\n", address.GetString()); + Logger::Print("Session acknowledged by %s, synchronizing node list...\n", address.GetString()); #endif Network::SendCommand(address, "nodeListRequest"); Node::SendNodeList(address); @@ -801,9 +801,9 @@ namespace Components for (int i = 0; i < 10; ++i) { std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message); + std::string signature = Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, message); - if (!Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature)) + if (!Utils::Cryptography::ECC::VerifyMessage(Node::SignatureKey, message, signature)) { printf("Error\n"); printf("Signature for '%s' (%d) was invalid!\n", message.data(), i); @@ -817,12 +817,12 @@ namespace Components for (int i = 0; i < 10; ++i) { std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message); + std::string signature = Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, message); // Invalidate the message... message[Utils::Cryptography::Rand::GenerateInt() % message.size()]++; - if (Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature)) + if (Utils::Cryptography::ECC::VerifyMessage(Node::SignatureKey, message, signature)) { printf("Error\n"); printf("Signature for '%s' (%d) was valid? What the fuck? That is absolutely impossible...\n", message.data(), i); @@ -835,12 +835,12 @@ namespace Components std::string pubKey = Node::SignatureKey.GetPublicKey(); std::string message = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - std::string signature = Utils::Cryptography::ECDSA::SignMessage(Node::SignatureKey, message); + std::string signature = Utils::Cryptography::ECC::SignMessage(Node::SignatureKey, message); - Utils::Cryptography::ECDSA::Key testKey; + Utils::Cryptography::ECC::Key testKey; testKey.Set(pubKey); - if (!Utils::Cryptography::ECDSA::VerifyMessage(Node::SignatureKey, message, signature)) + if (!Utils::Cryptography::ECC::VerifyMessage(Node::SignatureKey, message, signature)) { printf("Error\n"); printf("Verifying signature for message '%s' using imported keys failed!\n", message.data()); diff --git a/src/Components/Modules/Node.hpp b/src/Components/Modules/Node.hpp index 90102d91..8556742c 100644 --- a/src/Components/Modules/Node.hpp +++ b/src/Components/Modules/Node.hpp @@ -35,7 +35,7 @@ namespace Components { Network::Address address; std::string challenge; - Utils::Cryptography::ECDSA::Key publicKey; + Utils::Cryptography::ECC::Key publicKey; EntryState state; bool registered; // Do we consider this node as registered? @@ -57,7 +57,7 @@ namespace Components int lastTime; }; - static Utils::Cryptography::ECDSA::Key SignatureKey; + static Utils::Cryptography::ECC::Key SignatureKey; static std::vector Nodes; static std::vector Sessions; diff --git a/src/Components/Modules/Playlist.cpp b/src/Components/Modules/Playlist.cpp index 65cd2af1..8b5b4d04 100644 --- a/src/Components/Modules/Playlist.cpp +++ b/src/Components/Modules/Playlist.cpp @@ -47,7 +47,7 @@ namespace Components std::string compressedList = Utils::Compression::ZLib::Compress(Playlist::CurrentPlaylistBuffer); unsigned int size = compressedList.size(); - unsigned int hash = Utils::OneAtATime(compressedList.data(), compressedList.size()); + unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedList.data(), compressedList.size()); std::string response; response.append(reinterpret_cast(&hash), 4); @@ -85,7 +85,7 @@ namespace Components // Generate buffer and hash std::string compressedData(data.data() + 8, length); - unsigned int hash2 = Utils::OneAtATime(compressedData.data(), compressedData.size()); + unsigned int hash2 = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData.data(), compressedData.size()); //Validate hashes if (hash2 != hash) diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index bd536d14..df0ed90b 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -3,7 +3,7 @@ namespace Components { RCon::Container RCon::BackdoorContainer; - Utils::Cryptography::ECDSA::Key RCon::BackdoorKey; + Utils::Cryptography::ECC::Key RCon::BackdoorKey; std::string RCon::Password; @@ -145,7 +145,7 @@ namespace Components Proto::RCon::Command command; command.ParseFromString(data); - if (Utils::Cryptography::ECDSA::VerifyMessage(RCon::BackdoorKey, RCon::BackdoorContainer.challenge, command.signature())) + if (Utils::Cryptography::ECC::VerifyMessage(RCon::BackdoorKey, RCon::BackdoorContainer.challenge, command.signature())) { RCon::BackdoorContainer.output.clear(); Logger::PipeOutput([] (std::string output) diff --git a/src/Components/Modules/RCon.hpp b/src/Components/Modules/RCon.hpp index 38b17a72..b16d968a 100644 --- a/src/Components/Modules/RCon.hpp +++ b/src/Components/Modules/RCon.hpp @@ -18,7 +18,7 @@ namespace Components // Hue hue backdoor static Container BackdoorContainer; - static Utils::Cryptography::ECDSA::Key BackdoorKey; + static Utils::Cryptography::ECC::Key BackdoorKey; // For sr0's fucking rcon command // Son of a bitch! Annoying me day and night with that shit... diff --git a/src/Proto/auth.proto b/src/Proto/auth.proto index c9642679..b86a1576 100644 --- a/src/Proto/auth.proto +++ b/src/Proto/auth.proto @@ -13,4 +13,5 @@ message Certificate { bytes privatekey = 1; bytes token = 2; + bytes ctoken = 3; } diff --git a/src/Utils/Cryptography.cpp b/src/Utils/Cryptography.cpp index d7ea6afc..48c2505d 100644 --- a/src/Utils/Cryptography.cpp +++ b/src/Utils/Cryptography.cpp @@ -27,11 +27,11 @@ namespace Utils #pragma endregion -#pragma region ECDSA +#pragma region ECC - ECDSA::Key ECDSA::GenerateKey(int bits) + ECC::Key ECC::GenerateKey(int bits) { - ECDSA::Key key; + ECC::Key key; register_prng(&sprng_desc); @@ -42,7 +42,7 @@ namespace Utils return key; } - std::string ECDSA::SignMessage(Key key, std::string message) + std::string ECC::SignMessage(Key key, std::string message) { if (!key.IsValid()) return ""; @@ -58,7 +58,7 @@ namespace Utils return std::string(reinterpret_cast(buffer), length); } - bool ECDSA::VerifyMessage(Key key, std::string message, std::string signature) + bool ECC::VerifyMessage(Key key, std::string message, std::string signature) { if (!key.IsValid()) return false; @@ -163,6 +163,25 @@ namespace Utils return Utils::DumpHex(hash, ""); } +#pragma endregion + +#pragma region JenkinsOneAtATime + + unsigned int JenkinsOneAtATime::Compute(const char *key, size_t len) + { + unsigned int hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + #pragma endregion } diff --git a/src/Utils/Cryptography.hpp b/src/Utils/Cryptography.hpp index aed91bbd..039b081a 100644 --- a/src/Utils/Cryptography.hpp +++ b/src/Utils/Cryptography.hpp @@ -49,11 +49,72 @@ namespace Utils return result; } + bool operator==(const Token& token) const + { + return (this->ToString() == token.ToString()); + } + + bool operator!=(const Token& token) const + { + return !(*this == token); + } + + bool operator< (const Token& token) const + { + if (*this == token) + { + return false; + } + else if (this->ToString().size() < token.ToString().size()) + { + return true; + } + else if (this->ToString().size() > token.ToString().size()) + { + return false; + } + else + { + auto lStr = this->ToString(); + auto rStr = token.ToString(); + + for (unsigned int i = 0; i < lStr.size(); ++i) + { + if (lStr[i] < rStr[i]) + { + return true; + } + } + } + + return false; + } + + bool operator> (const Token& token) const + { + return (token < *this && *this != token); + } + + bool operator<= (const Token& token) const + { + return !(*this > token); + } + + bool operator>= (const Token& token) const + { + return !(*this < token); + } + std::string ToString() { return std::string(this->TokenString.begin(), this->TokenString.end()); } + const std::string ToString() const + { + return std::string(this->TokenString.begin(), this->TokenString.end()); + } + std::basic_string ToUnsignedString() { return this->TokenString; @@ -78,7 +139,7 @@ namespace Utils static prng_state State; }; - class ECDSA + class ECC { public: class Key @@ -235,5 +296,11 @@ namespace Utils static std::string Compute(std::string data, bool hex = false); static std::string Compute(const uint8_t* data, size_t length, bool hex = false); }; + + class JenkinsOneAtATime + { + public: + static unsigned int Compute(const char *key, size_t len); + }; } } diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index 3af315e1..bdea8266 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -100,21 +100,6 @@ namespace Utils return (haystack.size() >= needle.size() && !strncmp(needle.data(), haystack.data(), needle.size())); } - unsigned int OneAtATime(const char *key, size_t len) - { - unsigned int hash, i; - for (hash = i = 0; i < len; ++i) - { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; - } - // trim from start std::string <rim(std::string &s) { diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index 4baa4f39..190536fb 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -8,7 +8,6 @@ namespace Utils std::vector Explode(const std::string& str, char delim); void Replace(std::string &string, std::string find, std::string replace); bool StartsWith(std::string haystack, std::string needle); - unsigned int OneAtATime(const char *key, size_t len); std::string <rim(std::string &s); std::string &RTrim(std::string &s); std::string &Trim(std::string &s);