Fix copyright date.
[become] / src / crypt.c
index 86ed605..1c400aa 100644 (file)
@@ -1,13 +1,13 @@
 /* -*-c-*-
  *
- * $Id: crypt.c,v 1.1 1997/07/21 13:47:51 mdw Exp $
+ * $Id: crypt.c,v 1.4 1998/01/12 16:45:55 mdw Exp $
  *
  * Cryptographic transfer of `become' requests
  *
- * (c) 1997 EBI
+ * (c) 1998 EBI
  */
 
-/*----- Licencing notice --------------------------------------------------*
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of `become'
  *
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with `become'; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with `become'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
 /*----- Revision history --------------------------------------------------*
  *
  * $Log: crypt.c,v $
- * Revision 1.1  1997/07/21 13:47:51  mdw
+ * Revision 1.4  1998/01/12 16:45:55  mdw
+ * Fix copyright date.
+ *
+ * Revision 1.3  1997/09/26 09:14:58  mdw
+ * Merged blowfish branch into trunk.
+ *
+ * Revision 1.2.2.1  1997/09/26 09:08:02  mdw
+ * Use the Blowfish encryption algorithm instead of IDEA.  This is partly
+ * because I prefer Blowfish (without any particularly strong evidence) but
+ * mainly because IDEA is patented and Blowfish isn't.
+ *
+ * Revision 1.2  1997/08/04 10:24:21  mdw
+ * Sources placed under CVS control.
+ *
+ * Revision 1.1  1997/07/21  13:47:51  mdw
  * Initial revision
  *
  */
 /* --- Unix headers --- */
 
 #include <sys/types.h>
+#include <sys/time.h>
 #include <unistd.h>
 #include <syslog.h>
 #include <fcntl.h>
 
 /* --- Local headers --- */
 
+#include "become.h"
+#include "blowfish.h"
 #include "config.h"
 #include "crypt.h"
 #include "icrypt.h"
-#include "idea.h"
 #include "md5.h"
+#include "noise.h"
+#include "rand.h"
 #include "tx.h"
 #include "utils.h"
 
 /*----- Magic numbers -----------------------------------------------------*/
 
-#define crypt__timeError 15            /* Seconds error to permit */
-#define crypt__seedBits 512            /* Number of random seed bits */
-
-/*----- Dump a block of data ----------------------------------------------*/
-
-/* --- @crypt__dump@ --- *
- *
- * Arguments:   @char *p@ = a string to display at the top of the message
- *              @unsigned char *buf@ = pointer to a buffer to display
- *              @size_t sz@ = size of the buffer
- *             @FILE *fp@ = file to dump on
- *
- * Returns:     --
- *
- * Use:         Dumps a block of data to the terminal.  This allows a
- *              programmer to examine the traffic between client and server,
- *              and possibly locate bugs.
- */
-
-#ifndef NDEBUG
-
-static void crypt__dump(const char *p, const unsigned char *buf,
-                       int sz, FILE *fp)
-{
-  int i;
-  fprintf(stderr,"+++ %s\n",p);
-  while (sz>0)
-  {
-    fprintf(stderr,"+++ ");
-    for (i=0;i<8;i++)
-    {
-      if (i<sz)
-        fprintf(stderr,"%02x ",buf[i] & 0xff);
-      else
-        fputs("** ",stderr);
-    }
-    fputs(": ",stderr);
-    for (i=0;i<8;i++)
-      putc(i<sz ? (isprint(buf[i]) ? buf[i] : '.') : '*',stderr);
-    putc('\n',stderr);
-    buf+=8;
-    sz-=8;
-  }
-  putc('\n',stderr);
-}
-
-#else
-
-#define crypt__dump(p, buf, sz, fp) ((void)0)
-
-#endif
+#define crypt__timeError 60            /* Seconds error to permit */
 
 /*----- Main code ---------------------------------------------------------*/
 
@@ -122,8 +90,6 @@ static void crypt__dump(const char *p, const unsigned char *buf,
  *
  * Arguments:  @const char *seedfile@ = pointer to name of seed file
  *             @unsigned char *k@ = our secret key
- *             @time_t t@ = the current time
- *             @pid_t pid@ = our process id
  *             @unsigned *sk@ = where to store the session key
  *             @unsigned char *iv@ = where to store the IV
  *
@@ -133,108 +99,65 @@ static void crypt__dump(const char *p, const unsigned char *buf,
  */
 
 static void crypt__sessionKey(const char *seedfile, unsigned char *k,
-                             time_t t, pid_t pid,
                              unsigned char *sk, unsigned char *iv)
 {
   FILE *fp;                            /* File handle for reading */
-  unsigned char s[crypt__seedBits / 8];        /* Random seed block */
-  unsigned char b[4 + 4];
-                                       /* For building the hash block */
+  struct flock l;
+  int ok = 1;
 
-  /* --- Read the old seed in --- *
+  /* --- Open the random seed file --- *
    *
-   * Interlock with other processes locking this file.
+   * If I can't manage that, create a new one.
    */
 
-  {
-#ifndef TEST_RIG
-    struct flock l;
-#endif
-
-    if ((fp = fopen(seedfile, "r+")) == 0)
-      die("couldn't open random seed file `%s': %s",
-         seedfile, strerror(errno));
-
-#ifndef TEST_RIG
-    l.l_type = F_WRLCK;
-    l.l_whence = SEEK_SET;
-    l.l_start = 0;
-    l.l_len = 0;
-    if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
-      die("couldn't lock random number file: %s", strerror(errno));
-#endif
-
-    tx_getBits(s, crypt__seedBits, fp);
+  if ((fp = fopen(seedfile, "r+")) == 0) {
+    ok = 0;
+    if ((fp = fopen(seedfile, "w+")) == 0)
+      die("can't create random number file: %s", strerror(errno));
+    rand_clear();
   }
 
-  crypt__dump("Initial seed", s, crypt__seedBits / 8, stdout);
-
-  /* --- Build a block of data to hash --- */
+  /* --- Lock the seed file against concurrency problems --- */
 
-  store32(b + 0, t);
-  store32(b + 4, pid);
+  l.l_type = F_WRLCK;
+  l.l_whence = SEEK_SET;
+  l.l_start = 0;
+  l.l_len = 0;
+  if (fcntl(fileno(fp), F_SETLKW, &l) < 0)
+    die("can't lock random number file: %s", strerror(errno));
 
-  /* --- Work out the session key --- */
+    /* --- Now read the file, and launder the seed --- */
 
-  {
-    md5 md;
+  if (ok)
+    rand_read(fp);
 
-    md5_init(&md);
-    md5_buffer(&md, s, sizeof(s));
-    md5_buffer(&md, b, sizeof(b));
-    md5_buffer(&md, k, IDEA_KEYSIZE);
-    md5_final(&md, sk);
-
-    crypt__dump("Session key", sk, 128 / 8, stdout);
-    burn(md);
-  }
-
-  /* --- Build the new seed and write it back again --- */
-
-  if (crypt__seedBits > 128) {
-    unsigned char tb[MD5_HASHSIZE];
-    int i;
-
-    memcpy(tb, s + crypt__seedBits / 8 - sizeof(tb), sizeof(tb));
-    memmove(s + sizeof(tb), s, crypt__seedBits / 8 - sizeof(tb));
-    memcpy(s, tb, sizeof(tb));
-    for (i = 0; i < sizeof(sk); i++)
-      s[i] ^= sk[i];
-    burn(tb);
-  }
-
-  /* --- Take the seed we have and hash it again to get an IV --- */
+  /* --- Encrypt the pool using the secret key --- */
 
   {
-    unsigned char mdv[MD5_HASHSIZE];
-    md5 md;
-
-    md5_init(&md);
-    md5_buffer(&md, b, sizeof(b));
-    md5_buffer(&md, k, IDEA_KEYSIZE);
-    md5_buffer(&md, s, sizeof(s));
-    md5_final(&md, mdv);
-    memcpy(iv, mdv, IDEA_BLKSIZE);
-    crypt__dump("IV", iv, IDEA_BLKSIZE, stdout);
-    burn(md); burn(mdv);
+    icrypt_job j;
+    icrypt_init(&j, k, BLOWFISH_KEYSIZE, 0);
+    rand_encrypt(&j);
+    burn(j);
   }
 
+  /* --- Generate the session key and IV --- */
 
-  /* --- Lock the file again --- *
-   *
-   * We're closing the file after we've finished, so we don't need to
-   * unlock it afterwards.
-   */
+  noise_acquire();
+  rand_extract(sk, BLOWFISH_KEYSIZE);
+  rand_extract(iv, BLOWFISH_BLKSIZE);
+
+  IF_TRACING(TRACE_CRYPTO,
+    traceblk(TRACE_CRYPTO, "crypto: session key:", sk, BLOWFISH_KEYSIZE);
+    traceblk(TRACE_CRYPTO, "crypto: initialisation vector:",
+            iv, BLOWFISH_BLKSIZE);
+  );
 
-  crypt__dump("Final seed", s, crypt__seedBits / 8, stdout);
+  /* --- Write the seed back --- */
 
   rewind(fp);
-  tx_putBits(s, crypt__seedBits, fp);
+  rand_write(fp);
   fclose(fp);
 
-  /* --- Destroy sensitive data --- */
-
-  burn(b); burn(s);
 }
 
 /* --- @crypt_packRequest@ --- *
@@ -259,12 +182,17 @@ void crypt_packRequest(request *rq, unsigned char *buff,
 {
   /* --- First, build the easy stuff in the block --- */
 
-  buff[crq_cryptType] = cryptType_idea;
+  buff[crq_cryptType] = cryptType_blowfish;
   store32(buff + crq_time, t);
   store32(buff + crq_pid, pid);
   store32(buff + crq_from, rq->from);
   store32(buff + crq_to, rq->to);
 
+  /* --- Now generate session keys and things --- */
+
+  crypt__sessionKey(file_RANDSEED, k, sk, buff + crq_iv);
+  memcpy(buff + crq_session, sk, BLOWFISH_KEYSIZE);
+
   /* --- The string causes a few problems --- *
    *
    * There's a good chance that the string will be a good deal shorter than
@@ -273,54 +201,49 @@ void crypt_packRequest(request *rq, unsigned char *buff,
    * version of this code used @strncpy@ which is even worse!)
    *
    * I'll fill the block with random (from @rand@(3) -- nothing too
-   * elaborate) and then encrypt it using IDEA in CFB mode, using the first
-   * few bytes as the key.  This should provide a sufficiently unpredictable
-   * background for the block.
+   * elaborate) and then encrypt it using Blowfish in CFB mode, using the
+   * first few bytes as the key.  This should provide a sufficiently
+   * unpredictable background for the block.
    */
 
   {
     icrypt_job j;
     unsigned char *p;
     unsigned u;
+    md5 md;
+    unsigned char qk[BLOWFISH_KEYSIZE];
 
     /* --- Initialise the buffer with junk --- */
 
     srand((unsigned int)(t ^ pid));    /* Seed the (bad) RNG */
     for (p = buff + crq_cmd; p < buff + crq_cmd + CMDLEN_MAX; p++) {
-      u = rand();
-      *p = u ^ (u >> 8);
+      u = rand(); *p = u ^ (u >> 8);
     }
 
     /* --- Now make the junk a whole lot harder to predict --- */
 
     p = buff + crq_cmd;
-    icrypt_init(&j, p, 0);
+    md5_init(&md); md5_buffer(&md, p, CMDLEN_MAX); md5_final(&md, qk);
+    icrypt_init(&j, qk, BLOWFISH_KEYSIZE, 0);
     icrypt_encrypt(&j, p, p, CMDLEN_MAX);
-    burn(j);
+    burn(j); burn(qk); burn(md);
 
     /* --- Copy the string into here --- */
 
     strcpy((char *)buff + crq_cmd, rq->cmd);
   }
 
-  /* --- Generate a session key --- */
-
-  {
-    crypt__sessionKey(file_RANDSEED, k, t, pid, sk, buff + crq_iv);
-    memcpy(buff + crq_session, sk, IDEA_KEYSIZE);
-  }
-
   /* --- Checksum the finished data --- */
 
   {
     md5 md;
-    unsigned char mdv[MD5_HASHSIZE];
+    unsigned char mdbuf[MD5_HASHSIZE];
 
     md5_init(&md);
     md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
-    md5_final(&md, mdv);
-    memcpy(buff + crq_check, mdv, 4);
-    burn(md); burn(mdv);
+    md5_final(&md, mdbuf);
+    memcpy(buff + crq_check, mdbuf, 4);
+    burn(md); burn(mdbuf);
   }
 
   /* --- Encrypt the block --- *
@@ -334,16 +257,33 @@ void crypt_packRequest(request *rq, unsigned char *buff,
   {
     icrypt_job j;
 
-    crypt__dump("request, before encryption", buff, crq_size, stdout);
+    T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
+               buff, crq_size); )
+
+    T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
+    T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
+               buff + crq_iv, BLOWFISH_BLKSIZE); )
+    T( traceblk(TRACE_CRYPTO, "crypto: session key:",
+               sk, BLOWFISH_KEYSIZE); )
+
+    icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
+
+    icrypt_encrypt(&j, buff + crq_session,
+                  buff + crq_session, BLOWFISH_KEYSIZE);
+    T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
+               buff + crq_session, BLOWFISH_KEYSIZE); )
+
+    icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
+               j.iv, BLOWFISH_BLKSIZE); )
 
-    icrypt_init(&j, k, buff + crq_iv);
-    icrypt_encrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
-    icrypt_reset(&j, sk, 0);
     icrypt_encrypt(&j, buff + crq_cipher,
                   buff + crq_cipher, crq_size - crq_cipher);
     burn(j);
 
-    crypt__dump("request, after encryption", buff, crq_size, stdout);
+    T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
+               buff, crq_size); )
   }
 }
 
@@ -367,7 +307,7 @@ int crypt_unpackRequest(request *rq, unsigned char *buff,
   {
     /* --- Check the encryption format --- */
 
-    if (buff[crq_cryptType] != cryptType_idea)
+    if (buff[crq_cryptType] != cryptType_blowfish)
       return (0);
   }
 
@@ -376,36 +316,55 @@ int crypt_unpackRequest(request *rq, unsigned char *buff,
 
     icrypt_job j;
 
-    crypt__dump("request, before decryption", buff, crq_size, stdout);
+    T( traceblk(TRACE_CRYPTO, "crypto: ciphertext request:",
+               buff, crq_size); )
+
+    T( traceblk(TRACE_CRYPTO, "crypto: master key:", k, BLOWFISH_KEYSIZE); )
+    T( traceblk(TRACE_CRYPTO, "crypto: initial iv:",
+               buff + crq_iv, BLOWFISH_BLKSIZE); )
+
+    icrypt_init(&j, k, BLOWFISH_KEYSIZE, buff + crq_iv);
+    T( traceblk(TRACE_CRYPTO, "crypto: job block:", &j, sizeof(j)); )
+
+    T( traceblk(TRACE_CRYPTO, "crypto: encrypted session key:",
+               buff + crq_session, BLOWFISH_KEYSIZE); )
+    icrypt_decrypt(&j, buff + crq_session,
+                  buff + crq_session, BLOWFISH_KEYSIZE);
+    memcpy(sk, buff + crq_session, BLOWFISH_KEYSIZE);
+    T( traceblk(TRACE_CRYPTO, "crypto: session key:",
+               sk, BLOWFISH_KEYSIZE); )
+
+    icrypt_reset(&j, sk, BLOWFISH_KEYSIZE, 0);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: partial iv:",
+               j.iv, BLOWFISH_BLKSIZE); )
 
-    icrypt_init(&j, k, buff + crq_iv);
-    icrypt_decrypt(&j, buff + crq_session, buff + crq_session, IDEA_KEYSIZE);
-    memcpy(sk, buff + crq_session, IDEA_KEYSIZE);
-    icrypt_reset(&j, sk, 0);
     icrypt_decrypt(&j, buff + crq_cipher,
                   buff + crq_cipher, crq_size - crq_cipher);
     icrypt_saveIV(&j, rpl + crp_iv);
 
-    memset(buff + crq_session, 0, IDEA_KEYSIZE); /* Burn, baby, burn */
-    burn(j);
+    T( traceblk(TRACE_CRYPTO, "crypto: plaintext request:",
+               buff, crq_size); )
 
-    crypt__dump("request, after decryption", buff, crq_size, stdout);
+    memset(buff + crq_session, 0, BLOWFISH_KEYSIZE); /* Burn, baby, burn */
+    burn(j);
   }
 
   {
     /* --- Check the validity of the data therein --- */
 
     md5 md;
-    unsigned char mdv[MD5_HASHSIZE];
+    unsigned char mdbuf[MD5_HASHSIZE];
 
     md5_init(&md);
     md5_buffer(&md, buff + crq_cipher, crq_check - crq_cipher);
-    md5_final(&md, mdv);
-    if (memcmp(mdv, buff + crq_check, 4) != 0) {
+    md5_final(&md, mdbuf);
+    if (memcmp(mdbuf, buff + crq_check, 4) != 0) {
       syslog(LOG_INFO, "packet rejected: bad checksum");
+      T( trace(TRACE_CRYPTO, "crypto: bad checksum on incoming request"); )
       return (0);
     }
-    burn(md); burn(mdv);
+    burn(md); burn(mdbuf);
   }
 
   {
@@ -424,6 +383,7 @@ int crypt_unpackRequest(request *rq, unsigned char *buff,
 
     if (t - u > crypt__timeError || u - t > crypt__timeError) {
       syslog(LOG_INFO, "packet rejected: bad time");
+      T( trace(TRACE_CRYPTO, "crypto: bad time on incoming request"); )
       return (0);
     }
     memcpy(rpl + crp_time, buff + crq_time, 8);
@@ -431,6 +391,7 @@ int crypt_unpackRequest(request *rq, unsigned char *buff,
 
   /* --- Done --- */
 
+  T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
   return (1);
 }
 
@@ -457,25 +418,28 @@ void crypt_packReply(unsigned char *buff, unsigned char *sk, int answer)
     /* --- Build the checksum --- */
 
     md5 md;
-    unsigned char mdv[MD5_HASHSIZE];
+    unsigned char mdbuf[MD5_HASHSIZE];
 
     md5_init(&md);
     md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
-    md5_final(&md, mdv);
-    memcpy(buff + crp_check, mdv, 4);
-    burn(md); burn(mdv);
+    md5_final(&md, mdbuf);
+    memcpy(buff + crp_check, mdbuf, 4);
+    burn(md); burn(mdbuf);
   }
 
   {
     /* --- Encrypt the buffer --- */
 
     icrypt_job j;
-    icrypt_init(&j, sk, buff + crp_iv);
-    crypt__dump("reply, before encryption", buff, crp_size, stdout);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
+
+    icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
     icrypt_encrypt(&j, buff + crp_cipher,
                   buff + crp_cipher, crp_size - crp_cipher);
-    crypt__dump("reply, after encryption", buff, crp_size, stdout);
     burn(j);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
   }
 }
 
@@ -498,28 +462,32 @@ int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
     /* --- Decrypt my reply block --- */
 
     icrypt_job j;
-    icrypt_init(&j, sk, buff + crp_iv);
-    crypt__dump("reply, before decryption", buff, crp_size, stdout);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: ciphertext reply:", buff, crp_size); )
+
+    icrypt_init(&j, sk, BLOWFISH_KEYSIZE, buff + crp_iv);
     icrypt_decrypt(&j, buff + crp_cipher,
                   buff + crp_cipher, crp_size - crp_cipher);
-    crypt__dump("reply, after decryption", buff, crp_size, stdout);
     burn(j);
+
+    T( traceblk(TRACE_CRYPTO, "crypto: plaintext reply:", buff, crp_size); )
   }
 
   {
     /* --- Check validity --- */
 
     md5 md;
-    unsigned char mdv[MD5_HASHSIZE];
+    unsigned char mdbuf[MD5_HASHSIZE];
     char b[8];
 
     /* --- Check the checksum --- */
 
     md5_init(&md);
     md5_buffer(&md, buff + crp_cipher, crp_check - crp_cipher);
-    md5_final(&md, mdv);
-    if (memcmp(buff + crp_check, mdv, 4) != 0) {
+    md5_final(&md, mdbuf);
+    if (memcmp(buff + crp_check, mdbuf, 4) != 0) {
       syslog(LOG_INFO, "reply rejected: bad checksum");
+      T( trace(TRACE_CRYPTO, "crypto: bad checksum on reply"); )
       return (-1);
     }
 
@@ -528,12 +496,14 @@ int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
     store32(b + 0, t); store32(b + 4, pid);
     if (memcmp(b, buff + crp_time, sizeof(b)) != 0) {
       syslog(LOG_INFO, "reply rejected: bad identification marker");
+      T( trace(TRACE_CRYPTO, "crypto: bad id on reply"); )
       return (-1);
     }
   }
 
   /* --- Return the value --- */
 
+  T( trace(TRACE_CRYPTO, "crypto: valid reply received"); )
   return (buff[crp_answer]);
 }
 
@@ -543,13 +513,12 @@ int crypt_unpackReply(unsigned char *buff, unsigned char *sk,
 
 int main(int argc, char *argv[])
 {
-  time_t t = time(0);
-  pid_t pid = getpid();
   unsigned char buff[8];
-  unsigned char sk[IDEA_KEYSIZE], k[IDEA_KEYSIZE];
+  unsigned char sk[BLOWFISH_KEYSIZE], k[BLOWFISH_KEYSIZE];
   FILE *fp;
 
   ego(argv[0]);
+  traceon(stdout, TRACE_CRYPTO);
   if (argc < 3)
     die("bad args");
   fp = fopen(argv[1], "r");
@@ -557,7 +526,7 @@ int main(int argc, char *argv[])
     die("fopen: %s", strerror(errno));
   tx_getBits(k, 128, fp);
   fclose(fp);
-  crypt__sessionKey(argv[2], k, t, pid, sk, buff);
+  crypt__sessionKey(argv[2], k, sk, buff);
   return (0);
 }