Index: src/blob.c
==================================================================
--- src/blob.c
+++ src/blob.c
@@ -311,10 +311,36 @@
   sz = szA<szB ? szA : szB;
   rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz);
   if( rc==0 ){
     rc = szA - szB;
   }
+  return rc;
+}
+
+/*
+** Compare two blobs in constant time and return zero if they are equal.
+** Constant time comparison only applies for blobs of the same length.
+** If lengths are different, immediately returns 1.
+*/
+int blob_constant_time_cmp(Blob *pA, Blob *pB){
+  int szA, szB, i;
+  unsigned char *buf1, *buf2;
+  unsigned char rc = 0;
+
+  blob_is_init(pA);
+  blob_is_init(pB);
+  szA = blob_size(pA);
+  szB = blob_size(pB);
+  if( szA!=szB || szA==0 ) return 1;
+
+  buf1 = blob_buffer(pA);
+  buf2 = blob_buffer(pB);
+
+  for( i=0; i<szA; i++ ){
+    rc = rc | (buf1[i] ^ buf2[i]);
+  }
+
   return rc;
 }
 
 /*
 ** Compare a blob to a string.  Return TRUE if they are equal.

Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -230,12 +230,14 @@
   }
   if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
     /* The user requests a password change */
     zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
     if( db_int(1, "SELECT 0 FROM user"
-                  " WHERE uid=%d AND (pw=%Q OR pw=%Q)", 
-                  g.userUid, zPasswd, zSha1Pw) ){
+                  " WHERE uid=%d"
+                  " AND (constant_time_cmp(pw,%Q)=0"
+                  "      OR constant_time_cmp(pw,%Q)=0)", 
+                  g.userUid, zSha1Pw, zPasswd) ){
       sleep(1);
       zErrMsg = 
          @ <p><span class="loginError">
          @ You entered an incorrect old password while attempting to change
          @ your password.  Your password is unchanged.
@@ -308,12 +310,12 @@
     uid = db_int(0,
         "SELECT uid FROM user"
         " WHERE login=%Q"
         "   AND length(cap)>0 AND length(pw)>0"
         "   AND login NOT IN ('anonymous','nobody','developer','reader')"
-        "   AND (pw=%Q OR pw=%Q)",
-        zUsername, zPasswd, zSha1Pw
+        "   AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
+        zUsername, zSha1Pw, zPasswd
     );
     if( uid<=0 ){
       sleep(1);
       zErrMsg = 
          @ <p><span class="loginError">
@@ -451,10 +453,37 @@
     @ </table>
     @ </form>
   }
   style_footer();
 }
+
+/*
+** SQL function for constant time comparison of two values.
+** Sets result to 0 if two values are equal.
+*/
+static void constant_time_cmp_function(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+  const unsigned char *buf1, *buf2;
+  int len, i;
+  unsigned char rc = 0;
+
+  assert( argc==2 );
+  len = sqlite3_value_bytes(argv[0]);
+  if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
+    rc = 1;
+  }else{
+    buf1 = sqlite3_value_text(argv[0]);
+    buf2 = sqlite3_value_text(argv[1]);
+    for( i=0; i<len; i++ ){
+      rc = rc | (buf1[i] ^ buf2[i]);
+    }
+  }
+  sqlite3_result_int(context, rc);
+}
 
 /*
 ** Attempt to find login credentials for user zLogin on a peer repository
 ** with project code zCode.  Transfer those credentials to the local 
 ** repository.
@@ -481,20 +510,22 @@
   if( zOtherRepo==0 ) return 0;  /* No such peer repository */
 
   rc = sqlite3_open(zOtherRepo, &pOther);
   if( rc==SQLITE_OK ){
     sqlite3_create_function(pOther,"now",0,SQLITE_ANY,0,db_now_function,0,0);
+    sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0,
+		  constant_time_cmp_function, 0, 0);
     sqlite3_busy_timeout(pOther, 5000);
     zSQL = mprintf(
       "SELECT cexpire FROM user"
-      " WHERE cookie=%Q"
+      " WHERE login=%Q"
       "   AND ipaddr=%Q"
-      "   AND login=%Q"
       "   AND length(cap)>0"
       "   AND length(pw)>0"
-      "   AND cexpire>julianday('now')",
-      zHash, zRemoteAddr, zLogin
+      "   AND cexpire>julianday('now')"
+      "   AND constant_time_cmp(cookie,%Q)=0",
+      zLogin, zRemoteAddr, zHash
     );
     pStmt = 0;
     rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
     if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
       db_multi_exec(
@@ -527,16 +558,16 @@
   if( fossil_strcmp(zLogin, "developer")==0 ) return 0;
   if( fossil_strcmp(zLogin, "reader")==0 ) return 0;
   uid = db_int(0, 
     "SELECT uid FROM user"
     " WHERE login=%Q"
-    "   AND cookie=%Q"
     "   AND ipaddr=%Q"
     "   AND cexpire>julianday('now')"
     "   AND length(cap)>0"
-    "   AND length(pw)>0",
-    zLogin, zCookie, zRemoteAddr
+    "   AND length(pw)>0"
+    "   AND constant_time_cmp(cookie,%Q)=0",
+    zLogin, zRemoteAddr, zCookie
   );
   return uid;
 }
 
 /*
@@ -554,10 +585,13 @@
   char *zRemoteAddr;            /* Abbreviated IP address of the requestor */
   const char *zCap = 0;         /* Capability string */
 
   /* Only run this check once.  */
   if( g.userUid!=0 ) return;
+
+  sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
+		  constant_time_cmp_function, 0, 0);
 
   /* If the HTTP connection is coming over 127.0.0.1 and if
   ** local login is disabled and if we are using HTTP and not HTTPS, 
   ** then there is no need to check user credentials.
   **

Index: src/xfer.c
==================================================================
--- src/xfer.c
+++ src/xfer.c
@@ -573,11 +573,11 @@
     blob_zero(&combined);
     blob_copy(&combined, pNonce);
     blob_append(&combined, blob_buffer(&pw), szPw);
     sha1sum_blob(&combined, &hash);
     assert( blob_size(&hash)==40 );
-    rc = blob_compare(&hash, pSig);
+    rc = blob_constant_time_cmp(&hash, pSig);
     blob_reset(&hash);
     blob_reset(&combined);
     if( rc!=0 && szPw!=40 ){
       /* If this server stores cleartext passwords and the password did not
       ** match, then perhaps the client is sending SHA1 passwords.  Try
@@ -588,11 +588,11 @@
       blob_zero(&combined);
       blob_copy(&combined, pNonce);
       blob_append(&combined, zSecret, -1);
       free(zSecret);
       sha1sum_blob(&combined, &hash);
-      rc = blob_compare(&hash, pSig);
+      rc = blob_constant_time_cmp(&hash, pSig);
       blob_reset(&hash);
       blob_reset(&combined);
     }
     if( rc==0 ){
       const char *zCap;