|
@@ -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
|