Check-in [9a23c348b1]
Not logged in
Overview

SHA1 Hash:9a23c348b1bb1daa7a7a8517f1bc27862a2655c9
Date: 2009-03-27 14:32:33
User: drh
Comment:Infrastructure in place on the client side to encrypt sync traffic. This is mostly untested so far because we do not yet have a server that understands encrypted traffic.
Timelines: ancestors | descendants | both | experimental
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/blob.c from [2ed932bfd8] to [10f9b677e2].

@@ -929,17 +929,20 @@
 ** Because of the prepended nonce, the output will be 20 bytes larger
 ** than the input.
 **
 ** pOut should be initialized prior to invoking this routine.  pOut might
 ** already contain other content.  The encryption is appended to pOut.
+** The encryption cannot be done in place; pOut cannot be the same blob
+** as pIn.
 */
 void blob_encrypt(Blob *pIn, const char *zPassword, Blob *pOut){
   char *aNonce;
   char *aIn;
   int nIn;
   char *aOut;
 
+  assert( pIn!=pOut );
   aIn = pIn->aData;
   aIn += pIn->iCursor;
   nIn = pIn->nUsed - pIn->iCursor;
   if( nIn<=0 ) return;
   blob_resize(pOut, pOut->nUsed+nIn+N_NONCE);
@@ -961,11 +964,13 @@
 ** Only that portion of pIn beginning at the current cursor location and
 ** extending to the end of the blob is decrypted.  Any content of pIn
 ** prior to the current cursor position is ignored.
 **
 ** pOut should be initialized prior to invoking this routine.  pOut might
-** already contain other content.  The encryption is appended to pOut.
+** already contain other content.  The decryption is appended to pOut
+** starting at its current cursor position.  Decryption can be done
+** in place; it is acceptable for pOut and pIn to be the same blob.
 */
 void blob_decrypt(Blob *pIn, const char *zPassword, Blob *pOut){
   char *aNonce;
   char *aIn;
   int nIn;
@@ -973,14 +978,21 @@
 
   aIn = pIn->aData;
   aNonce = aIn + pIn->iCursor;
   aIn = aNonce + N_NONCE;
   nIn = pIn->nUsed - pIn->iCursor - N_NONCE;
-  blob_resize(pOut, pOut->iCursor + nIn);
+  if( pOut!=pIn ){
+    blob_resize(pOut, pOut->iCursor + nIn);
+  }
   aOut = pOut->aData;
   aOut += pOut->iCursor;
   rc4_coder(zPassword, aNonce, N_NONCE, aIn, nIn, aOut);
+  if( pOut==pIn ){
+    pOut->nUsed = pOut->iCursor + nIn;
+    aOut[nIn] = 0;
+    pOut->iCursor = pOut->nUsed;
+  }
 }
 
 
 /*
 ** COMMAND: test-encrypt PASSWORD PLAINTEXT CYPHERTEXT

Modified src/http.c from [a7c94bef96] to [2a16b7f520].

@@ -172,16 +172,37 @@
   return blob_size(pBlob);
 }
 #endif
 
 /*
+** zLine is a line from the header of an HTTP reply.  Find the
+** "argument" to this header line.  The argument is the first non-whitespace
+** character following the first ":" in the line.  The argument consists of
+** non-whitespace and non-semicolon characters.  Zero-terminate the argument
+** before returning.
+*/
+static char *header_arg(char *zIn){
+  char *z;
+  while( *zIn && *zIn!=':' ){ zIn++; }
+  if( *zIn==':' ){ zIn++; }
+  while( isspace(*zIn) ){ zIn++; }
+  for(z=zIn; *z && *z!=';' && !isspace(*z); z++){}
+  *z = 0;
+  return zIn;
+}
+
+/*
 ** Make a single attempt to talk to the server.  Return TRUE on success
 ** and FALSE on a failure.
 **
 ** pHeader contains the HTTP header.  pPayload contains the content.
 ** The content of the reply is written into pReply.  pReply is assumed
 ** to be uninitialized prior to this call.
+**
+** pPayload has already been compressed and/or encrypted prior to being
+** passed into this function.  pReply will be decrypted and/or decompressed
+** as required by this function, based on the Content-Type of the reply.
 **
 ** If an error occurs, this routine return false, resets pReply and
 ** closes the persistent connection, if any.
 */
 static int http_send_recv(Blob *pHeader, Blob *pPayload, Blob *pReply){
@@ -189,10 +210,12 @@
   int rc;
   int iLength;
   int iHttpVersion;
   int i;
   int nRead;
+  int needDecrypt = 0;
+  int needDecompress = 0;
   char zLine[2000];
 
   if( pSocket==0 && http_open_socket() ){
     return 0;
   }
@@ -219,18 +242,27 @@
       if( iHttpVersion==0 ){
         closeConnection = 1;
       }else{
         closeConnection = 0;
       }
-    } else if( strncasecmp(zLine, "content-length:", 15)==0 ){
-      iLength = atoi(&zLine[16]);
+    }else if( strncasecmp(zLine,"content-length:",15)==0 ){
+      iLength = atoi(header_arg(zLine));
     }else if( strncasecmp(zLine, "connection:", 11)==0 ){
-      for(i=12; isspace(zLine[i]); i++){}
-      if( zLine[i]=='c' || zLine[i]=='C' ){
-        closeConnection = 1;
-      }else if( zLine[i]=='k' || zLine[i]=='K' ){
-        closeConnection = 0;
+      int c = header_arg(zLine)[0];
+      if( c=='c' || c=='C' ){
+        closeConnection = 1;   /* Connection: close */
+      }else if( c=='k' || c=='K' ){
+        closeConnection = 0;   /* Connection: keep-alive */
+      }
+    }else if( strncasecmp(zLine, "content-type:", 13)==0 ){
+      const char *zType = header_arg(zLine);
+      if( strcmp(zType, "application/x-fossil")==0 ){
+        needDecompress = 1;
+        needDecrypt = 0;
+      }else if( strcmp(zType, "application/x-fossil-secure")==0 ){
+        needDecompress = 1;
+        needDecrypt = 1;
       }
     }
   }
   if( iLength<0 ) goto write_err;
   nRead = socket_read_blob(pReply, pSocket, iLength);
@@ -251,17 +283,26 @@
   while( fgets(zLine, sizeof(zLine), pSocket) ){
     for(i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
     if( i==0 ) break;
     zLine[i] = 0;
     if( strncasecmp(zLine,"content-length:",15)==0 ){
-      iLength = atoi(&zLine[16]);
+      iLength = atoi(header_arg(zLine));
     }else if( strncasecmp(zLine, "connection:", 11)==0 ){
-      for(i=12; isspace(zLine[i]); i++){}
-      if( zLine[i]=='c' || zLine[i]=='C' ){
+      int c = header_arg(zLine)[0];
+      if( c=='c' || c=='C' ){
         closeConnection = 1;   /* Connection: close */
-      }else if( zLine[i]=='k' || zLine[i]=='K' ){
+      }else if( c=='k' || c=='K' ){
         closeConnection = 0;   /* Connection: keep-alive */
+      }
+    }else if( strncasecmp(zLine, "content-type:", 13)==0 ){
+      const char *zType = header_arg(zLine);
+      if( strcmp(zType, "application/x-fossil")==0 ){
+        needDecompress = 1;
+        needDecrypt = 0;
+      }else if( strcmp(zType, "application/x-fossil-secure")==0 ){
+        needDecompress = 1;
+        needDecrypt = 1;
       }
     }
   }
   if( iLength<0 ) goto write_err;
   nRead = blob_read_from_channel(pReply, pSocket, iLength);
@@ -270,10 +311,16 @@
     blob_reset(pReply);
     goto write_err;
   }
   if( closeConnection ){
     http_close();
+  }
+  if( needDecrypt ){
+    blob_decrypt(pReply, g.urlPasswd, pReply);
+  }
+  if( needDecompress ){
+    blob_uncompress(pReply, pReply);
   }
   return 1;
 
 write_err:
   http_close();
@@ -341,11 +388,15 @@
   blob_reset(&pw);
   blob_reset(&sig);
 
   /* Construct the payload, which includes the login card.
   */
-  if( g.fHttpTrace ){
+  if( g.fHttpSecure && g.urlPasswd[0] ){
+    blob_compress(pSend, pSend);
+    payload = login;
+    blob_encrypt(pSend, g.urlPasswd, &payload);
+  }else if( g.fHttpTrace ){
     payload = login;
     blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
   }else{
     blob_compress2(&login, pSend, &payload);
     blob_reset(&login);
@@ -361,11 +412,13 @@
     zSep = "/";
   }
   blob_appendf(&hdr, "POST %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep);
   blob_appendf(&hdr, "Host: %s\r\n", g.urlHostname);
   blob_appendf(&hdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
-  if( g.fHttpTrace ){
+  if( g.fHttpSecure && g.urlPasswd[0] ){
+    blob_appendf(&hdr, "Content-Type: application/x-fossil-secure\r\n");
+  }else if( g.fHttpTrace ){
     blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n");
   }else{
     blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n");
   }
   blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));
@@ -403,16 +456,15 @@
   }
   blob_reset(&hdr);
   blob_reset(&payload);
 
   /* Process the reply.  pRecv contains only the payload of the
-  ** reply message, not the header.
+  ** reply message, not the header.  pRecv has already been decrypted
+  ** and decompressed
   */
   if( g.fHttpTrace ){
     printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pRecv));
-  }else{
-    blob_uncompress(pRecv, pRecv);
   }
 }
 
 
 /*

Modified src/main.c from [828c799fea] to [ac2f572b24].

@@ -62,10 +62,11 @@
   char *zLocalRoot;       /* The directory holding the  local database */
   int minPrefix;          /* Number of digits needed for a distinct UUID */
   int fSqlTrace;          /* True if -sqltrace flag is present */
   int fSqlPrint;          /* True if -sqlprint flag is present */
   int fHttpTrace;         /* Trace outbound HTTP requests */
+  int fHttpSecure;        /* Encrypt sync traffic */
   int fNoSync;            /* Do not do an autosync even.  --nosync */
   char *zPath;            /* Name of webpage being served */
   char *zExtra;           /* Extra path information past the webpage name */
   char *zBaseURL;         /* Full text of the URL being served */
   char *zTop;             /* Parent directory of zPath */