/* -*-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 ---------------------------------------------------------*/
*
* 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
*
*/
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@ --- *
{
/* --- 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
* 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 --- *
{
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); )
}
}
{
/* --- Check the encryption format --- */
- if (buff[crq_cryptType] != cryptType_idea)
+ if (buff[crq_cryptType] != cryptType_blowfish)
return (0);
}
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);
}
{
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);
/* --- Done --- */
+ T( trace(TRACE_CRYPTO, "crypto: valid request received"); )
return (1);
}
/* --- 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); )
}
}
/* --- 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);
}
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]);
}
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");
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);
}