// ---------------------------------------------------------------------- // File: Authenticator.cc // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "redis/Authenticator.hh" #include "utils/Macros.hh" #include "utils/Random.hh" #include "utils/StringUtils.hh" #include #include #include namespace quarkdb { Authenticator::Authenticator(std::string_view secret) : secretKey(secret) { if(!secret.empty() && secret.size() < 32) { qdb_throw("Secret key is too small! Minimum size: 32"); } } std::string Authenticator::generateChallenge(std::string_view opponentRandomBytes, std::chrono::system_clock::time_point timestamp, std::string_view myRandomBytes) { qdb_assert(opponentRandomBytes != myRandomBytes); // Calculate the deadline - responses will not be accepted after this much time // has elapsed. std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); challengeDeadline = now + std::chrono::minutes(1); // Convert the timepoint to string std::string strTimePoint = std::to_string( std::chrono::duration_cast(timestamp.time_since_epoch()).count() ); qdb_assert(opponentRandomBytes.size() == 64); qdb_assert(myRandomBytes.size() == 64); challengeString = SSTR(opponentRandomBytes << "---" << strTimePoint << "---" << myRandomBytes); return challengeString; } std::string Authenticator::generateChallenge(std::string_view opponentRandomBytes) { qdb_assert(opponentRandomBytes.size() == 64); // Calculate a timepoint based on system_clock to make the challenge more difficult. // We don't use steady_clock, as that leaks information (machine uptime) to // unauthorized users. (not really important, but let's be paranoid) return generateChallenge( opponentRandomBytes, std::chrono::system_clock::now(), generateSecureRandomBytes(64) ); } std::string Authenticator::generateSignature(std::string_view stringToSign, std::string_view key) { std::string ret; ret.resize(SHA256_DIGEST_LENGTH); unsigned int bufferLen = SHA256_DIGEST_LENGTH; HMAC(EVP_sha256(), (const unsigned char*) key.data(), key.size(), (const unsigned char*) stringToSign.data(), stringToSign.size(), (unsigned char*) ret.data(), &bufferLen); return ret; } Authenticator::ValidationStatus Authenticator::validateSignature(std::string_view signature) { std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); auto deadline = challengeDeadline; resetDeadline(); if(deadline < now) { return ValidationStatus::kDeadlinePassed; } if(signature != Authenticator::generateSignature(challengeString, secretKey)) { return ValidationStatus::kInvalidSignature; } return ValidationStatus::kOk; } void Authenticator::resetDeadline() { challengeDeadline = {}; } }