CVE-2013-10006: Observable Timing Discrepancy (CWE-208)
value: A vulnerability classified as problematic was found in Ziftr primecoin up to 0.8.4rc1. Affected by this vulnerability is the function HTTPAuthorized of the file src/bitcoinrpc.cpp. The manipulation of the argument strUserPass/strRPCUserColonPass leads to observable timing discrepancy. The complexity of an attack is rather high. The exploitation appears to be difficult. Upgrading to version 0.8.4rc2 is able to address this issue. The patch is named cdb3441b5cd2c1bae49fae671dc4a496f7c96322. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-217171.
CWE description: Two separate operations in a product require different amounts of time to complete, in a way that is observable to an actor and reveals security-relevant information about the state of the product, such as whether a particular operation was successful or not.
CVSS v3 base score: 7.5
Published: 2023-01-01T17:15Z
Repository: https://github.com/Ziftr/primecoin
Language: C++
Files changed: src/bitcoinrpc.cpp, src/test/util_tests.cpp, src/util.h
Commit date: 2013-08-20 12:19:40 +1000
Commit message: Make RPC password resistant to timing attacks

Fixes issue#2838; this is a tweaked version of pull#2845 that
should not leak the length of the password and is more generic,
in case we run into other situations where we need
timing-attack-resistant comparisons.

Diff: diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 2c4744a5..31452fa1 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -477,11 +477,11 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
     string strAuth = mapHeaders["authorization"];
     if (strAuth.substr(0,6) != "Basic ")
         return false;
     string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
     string strUserPass = DecodeBase64(strUserPass64);
-    return strUserPass == strRPCUserColonPass;
+    return TimingResistantEqual(strUserPass, strRPCUserColonPass);
 }
 
 //
 // JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 1b0ccad5..9ca0eef7 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -321,6 +321,17 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
         for (i=0;i<10000;i++) count += insecure_rand()%mod==0;
         BOOST_CHECK_EQUAL(count,exp_count[mod-2]);
     }
 }
 
+BOOST_AUTO_TEST_CASE(util_TimingResistantEqual)
+{
+    BOOST_CHECK(TimingResistantEqual(std::string(""), std::string("")));
+    BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("")));
+    BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc")));
+    BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa")));
+    BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a")));
+    BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc")));
+    BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba")));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util.h b/src/util.h
index 3f3dd0f4..9b7e2573 100644
--- a/src/util.h
+++ b/src/util.h
@@ -431,10 +431,25 @@ static inline uint32_t insecure_rand(void)
  * Seed insecure_rand using the random pool.
  * @param Deterministic Use a determinstic seed
  */
 void seed_insecure_rand(bool fDeterministic=false);
 
+/**
+ * Timing-attack-resistant comparison.
+ * Takes time proportional to length
+ * of first argument.
+ */
+template <typename T>
+bool TimingResistantEqual(const T& a, const T& b)
+{
+    if (b.size() == 0) return a.size() == 0;
+    size_t accumulator = a.size() ^ b.size();
+    for (size_t i = 0; i < a.size(); i++)
+        accumulator |= a[i] ^ b[i%b.size()];
+    return accumulator == 0;
+}
+
 /** Median filter over a stream of values.
  * Returns the median of the last N numbers
  */
 template <typename T> class CMedianFilter
 {

Vulnerable code:     return strUserPass == strRPCUserColonPass;
Fixed code:     return TimingResistantEqual(strUserPass, strRPCUserColonPass);
BOOST_AUTO_TEST_CASE(util_TimingResistantEqual)
{
    BOOST_CHECK(TimingResistantEqual(std::string(""), std::string("")));
    BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("")));
    BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc")));
    BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa")));
    BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a")));
    BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc")));
    BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba")));
}

/**
 * Timing-attack-resistant comparison.
 * Takes time proportional to length
 * of first argument.
 */
template <typename T>
bool TimingResistantEqual(const T& a, const T& b)
{
    if (b.size() == 0) return a.size() == 0;
    size_t accumulator = a.size() ^ b.size();
    for (size_t i = 0; i < a.size(); i++)
        accumulator |= a[i] ^ b[i%b.size()];
    return accumulator == 0;
}