Browse code

auth: prevent side-channel (timing) attack on UAS auth

Stefan Sayer authored on 15/04/2014 10:16:49
Showing 3 changed files
... ...
@@ -293,6 +293,30 @@ void w_MD5Update(MD5_CTX *ctx, const string& s) {
293 293
   MD5Update(ctx, a, s.length());
294 294
 }
295 295
 
296
+/** time-constant string compare function, but leaks timing of length mismatch */
297
+bool UACAuth::tc_isequal(const std::string& s1, const std::string& s2) {
298
+  if (s1.length() != s2.length())
299
+    return false;
300
+
301
+  bool res = false;
302
+
303
+  for (size_t i=0;i<s1.length();i++)
304
+    res |= s1[i]^s2[i];
305
+
306
+  return !res;
307
+}
308
+
309
+/** time-constant string compare function, but leaks timing of length mismatch */
310
+bool UACAuth::tc_isequal(const char* s1, const char* s2, size_t len) {
311
+
312
+  bool res = false;
313
+
314
+  for (size_t i=0;i<len;i++)
315
+    res |= s1[i]^s2[i];
316
+
317
+  return !res;
318
+}
319
+
296 320
 // supr gly
297 321
 string UACAuth::find_attribute(const string& name, const string& header) {
298 322
   size_t pos1 = header.find(name);
... ...
@@ -597,7 +621,7 @@ bool UACAuth::checkNonce(const string& nonce) {
597 621
   MD5Final(RespHash, &Md5Ctx);
598 622
   cvt_hex(RespHash, hash);
599 623
 
600
-  return !strncmp((const char*)hash, &nonce[INT_HEX_LEN], HASHHEXLEN);
624
+  return tc_isequal((const char*)hash, &nonce[INT_HEX_LEN], HASHHEXLEN);
601 625
 }
602 626
 
603 627
 void UACAuth::setServerSecret(const string& secret) {
... ...
@@ -709,7 +733,7 @@ void UACAuth::checkAuthentication(const AmSipRequest* req, const string& realm,
709 733
     uac_calc_response( ha1, ha2, r_challenge, r_cnonce, qop_value, client_nonce_count, response);
710 734
     DBG("calculated our response vs request: '%s' vs '%s'", response, r_response.c_str());
711 735
 
712
-    if (!strncmp((const char*)response, r_response.c_str(), HASHHEXLEN)) {
736
+    if (tc_isequal((const char*)response, r_response.c_str(), HASHHEXLEN)) {
713 737
       DBG("Auth: authentication successfull\n");
714 738
       authenticated = true;
715 739
     } else {
... ...
@@ -112,7 +112,7 @@ class UACAuth : public AmSessionEventHandler
112 112
   unsigned int nonce_count;
113 113
 
114 114
   bool nonce_reuse; // reused nonce?
115
-
115
+ 
116 116
   static std::string find_attribute(const std::string& name, const std::string& header);
117 117
   static bool parse_header(const std::string& auth_hdr, UACAuthDigestChallenge& challenge);
118 118
 
... ...
@@ -171,6 +171,13 @@ class UACAuth : public AmSessionEventHandler
171 171
 				  const string& user, const string& pwd, AmArg& ret);
172 172
 
173 173
   static void setServerSecret(const string& secret);
174
+
175
+  /** time-constant string compare function (but leaks timing of length mismatch)
176
+      @return true if matching */
177
+  static bool tc_isequal(const std::string& s1, const std::string& s2);
178
+  /** time-constant string compare function @return true if matching */
179
+  static bool tc_isequal(const char* s1, const char* s2, size_t len);
180
+
174 181
 };
175 182
 
176 183
 
... ...
@@ -48,4 +48,34 @@ FCTMF_SUITE_BGN(test_auth) {
48 48
       fct_chk( !UACAuth::checkNonce(nonce));
49 49
     } FCT_TEST_END();
50 50
 
51
+    FCT_TEST_BGN(t_cmp_len) {
52
+      string s1 = "1234secret";
53
+      string s2 = "1234s3ecret";
54
+      fct_chk( !UACAuth::tc_isequal(s1,s2) );
55
+    } FCT_TEST_END();
56
+
57
+    FCT_TEST_BGN(t_cmp_eq) {
58
+      string s1 = "1234secret";
59
+      string s2 = "1234secret";
60
+      fct_chk( UACAuth::tc_isequal(s1,s2) );
61
+    } FCT_TEST_END();
62
+
63
+
64
+    FCT_TEST_BGN(t_cmp_empty) {
65
+      fct_chk( UACAuth::tc_isequal("","") );
66
+    } FCT_TEST_END();
67
+
68
+    FCT_TEST_BGN(t_cmp_uneq) {
69
+      fct_chk( !UACAuth::tc_isequal("1234secret","2134secret") );
70
+    } FCT_TEST_END();
71
+
72
+    FCT_TEST_BGN(t_cmp_uneq_chr) {
73
+      fct_chk( !UACAuth::tc_isequal("1234secret","2134secret", 10) );
74
+    } FCT_TEST_END();
75
+
76
+    FCT_TEST_BGN(t_cmp_eq_charptr) {
77
+      fct_chk( UACAuth::tc_isequal("1234secret","1234secret", 10) );
78
+    } FCT_TEST_END();
79
+
80
+
51 81
 } FCTMF_SUITE_END();