diff options
| author | Nils Maier <maierman@web.de> | 2014-04-19 19:10:06 +0200 |
|---|---|---|
| committer | Nils Maier <maierman@web.de> | 2014-04-19 19:10:06 +0200 |
| commit | 82dad90ff3fe1438ce5d578dcf50bc82e5c96e9b (patch) | |
| tree | d3c0e83c55f04b84bddddca37d7c15b3daaea5f2 /src/DownloadEngine.cc | |
| parent | 98ba096951247b7e2119afe6ba4e4ddd7d4a4485 (diff) | |
Validate token using PBKDF2-HMAC-SHA1.
This change should make token validation more resilient to:
- timing attacks (constant time array compare)
- brute-force/dictionary attacks (PBKDF2)
Closes #220
Diffstat (limited to 'src/DownloadEngine.cc')
| -rw-r--r-- | src/DownloadEngine.cc | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/src/DownloadEngine.cc b/src/DownloadEngine.cc index efa86950..36b67469 100644 --- a/src/DownloadEngine.cc +++ b/src/DownloadEngine.cc @@ -73,6 +73,15 @@ #ifdef ENABLE_WEBSOCKET # include "WebSocketSessionMan.h" #endif // ENABLE_WEBSOCKET +#include "Option.h" +#include "util_security.h" + +// Lower time limit for PBKDF2 operations in validateToken. +static const double kTokenTimeLower = 0.055; +// Upper time limit for PBKDF2 operations in validateToken. +static const double kTokenTimeUpper = 0.120; +// Sweet spot time for PBKDF2 operations in validateToken. +static const double kTokenTimeSweetspot = 0.072; namespace aria2 { @@ -102,7 +111,8 @@ DownloadEngine::DownloadEngine(std::unique_ptr<EventPoll> eventPoll) asyncDNSServers_(nullptr), #endif // HAVE_ARES_ADDR_NODE dnsCache_(make_unique<DNSCache>()), - option_(nullptr) + option_(nullptr), + tokenIterations_(5000) { unsigned char sessionId[20]; util::generateRandomKey(sessionId); @@ -631,4 +641,84 @@ void DownloadEngine::setWebSocketSessionMan } #endif // ENABLE_WEBSOCKET +bool DownloadEngine::validateToken(const std::string& token) +{ + using namespace util::security; + + if (!option_->defined(PREF_RPC_SECRET)) { + return true; + } + + if (!tokenHMAC_ || tokenAverageDuration_ > kTokenTimeUpper || + tokenAverageDuration_ < kTokenTimeLower) { + + // Setup our stuff. + if (tokenHMAC_) { + A2_LOG_INFO(fmt("Recalculating iterations because avg. duration is %.4f", + tokenAverageDuration_)); + } + + tokenHMAC_ = HMAC::createRandom(); + if (!tokenHMAC_) { + A2_LOG_ERROR("Failed to create HMAC"); + return false; + } + + // This should still be pretty fast on a modern system... Well, too fast + // with the initial 5000 iterations, and that is why we adjust it. + // XXX We should run this setup high priorty, so that other processes on the + // system don't mess up our results and let us underestimate the iterations. + std::deque<double> mm; + for (auto i = 0; i < 10; ++i) { + auto c = std::clock(); + tokenExpected_ = make_unique<HMACResult>( + PBKDF2(tokenHMAC_.get(), option_->get(PREF_RPC_SECRET), + tokenIterations_)); + mm.push_back((std::clock() - c) / (double)CLOCKS_PER_SEC); + } + std::sort(mm.begin(), mm.end()); + // Pop outliers. + mm.pop_front(); + mm.pop_back(); + mm.pop_back(); + auto duration = std::accumulate(mm.begin(), mm.end(), 0.0) / mm.size(); + + A2_LOG_INFO(fmt("Took us %.4f secs on average to perform PBKDF2 with %zu " + "iterations during setup", + duration, tokenIterations_)); + + // Adjust iterations so that an op takes about |kTokenTimeSpeetspot| sec, + // which would allow for a couple attempts per second (instead of + // potentially thousands without PBKDF2). + // We might overestimate the performance a bit, but should not perform + // worse than |kTokenTimeUpper| secs per attempt on a normally loaded system + // and no better than |kTokenTimeLower|. If this does not hold true anymore, + // the |tokenAverageDuration_| checks will force a re-calcuation. + tokenIterations_ *= kTokenTimeSweetspot / duration; + + auto c = std::clock(); + tokenExpected_ = make_unique<HMACResult>( + PBKDF2(tokenHMAC_.get(), option_->get(PREF_RPC_SECRET), + tokenIterations_)); + duration = (std::clock() - c) / (double)CLOCKS_PER_SEC; + A2_LOG_INFO(fmt("Took us %.4f secs to perform PBKDF2 with %zu iterations", + duration, tokenIterations_)); + + // Seed average duration. + tokenAverageDuration_ = duration; + } + + auto c = std::clock(); + bool rv = *tokenExpected_ == PBKDF2(tokenHMAC_.get(), token, + tokenIterations_); + auto duration = (std::clock() - c) / (double)CLOCKS_PER_SEC; + A2_LOG_DEBUG(fmt("Took us %.4f secs to perform token compare with %zu " + "iterations", + duration, tokenIterations_)); + + // Update rolling hash. + tokenAverageDuration_ = tokenAverageDuration_ * 0.9 + duration * 0.1; + return rv; +} + } // namespace aria2 |
