Interlock protocol chat program source code
Anonymous
discord-nobody at erisiandiscord.de
Sun Oct 5 22:40:42 EDT 2003
/* interchat.c
*
* Two player chat program using the Interlock Protocol
*
* Based on Rivest and Shamir, "How to expose an eavesdropper",
* Communications of the ACM, v 27 no 4 (Apr 1984), pp 393-395.
*
* Requires the free OpenSSL crypto library, from www.openssl.org.
*
* Warning: this is a quick hack written in about three hours, as a
* demonstration of the principle and not as bulletproof security code.
*
* Usage: First person starts the program listening for a connection
* with ./interchat portnum.
* Second person connects to him with ./interchat hostname portnum.
* Each party types a line of chat, and once each person has entered
* his line, the data is exchanged. No data will be received until each
* person has entered their next line.
*
* Under certain assumptions, this approach will detect man-in-the-middle
* attacks. Actually, this program can be used to try acting as a MITM:
* the MITM runs two chat sessions in different windows, connecting
* to the two "victims" who think they are talking with each other.
* By manipulating the two conversations he can try to get the two
* parties to talk and share information with each other such that
* he learns any secrets being exchanged.
*
* This software is
* Copyright (C) 2003 by PGP key ID AEE49232,
* fingerprint CC6BAB551D0D2B65BB0B6D981C801DF4AEE49232.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following condition
* is met:
*
* Redistributions of source code must retain the above copyright
* notice, this condition and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include "openssl/bn.h"
#include "openssl/sha.h"
#include "openssl/evp.h"
#include "openssl/hmac.h"
typedef unsigned char uchar;
#define PADLEN 16
#define MYBUFLEN 256
#define MIN(x,y) (((x)<(y))?(x):(y))
BN_CTX *bnctx;
/* DH parameters */
BIGNUM *p, *q, *g;
#define XBITS 200
/* Oakley group 5, RFC 2412 */
char *p_hex = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
"83655D23DCA3AD961C62F356208552BB9ED529077096966D"
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
char *q_hex = "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68"
"948127044533E63A0105DF531D89CD9128A5043CC71A026E"
"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122"
"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6"
"F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E"
"E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AF"
"C1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36"
"B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF";
char *g_hex = "2";
/* DH public and private values */
BIGNUM *my_x, *my_y, *his_y, *shared;
/* Encrypt/decrypt keys */
uchar enckey[16], deckey[16];
uchar mymackey[16], hismackey[16];
/* I/O functions */
static void
bnwrite( BIGNUM *bn, FILE *f )
{
unsigned char *p;
int len;
len = BN_bn2mpi( bn, NULL );
p = malloc( len );
BN_bn2mpi( bn, p );
fwrite( p, 1, len, f );
free( p );
fflush( f );
}
static void
bnread( BIGNUM **bn, FILE *f )
{
unsigned char *p;
int len;
unsigned char mpibuf[4];
if( fread( mpibuf, 1, sizeof(mpibuf), f ) != sizeof(mpibuf) )
{
perror("fread");
exit(1);
}
len = (mpibuf[0]<<24)|(mpibuf[1]<<16)|(mpibuf[2]<<8)|(mpibuf[3]);
p = malloc( len+sizeof(mpibuf) );
if( p==NULL )
{
perror("malloc");
exit(1);
}
memcpy(p, mpibuf, sizeof(mpibuf));
if( fread( p+sizeof(mpibuf), 1, len, f ) != len )
{
perror("fread");
exit(1);
}
*bn = BN_mpi2bn( p, len+sizeof(mpibuf), NULL );
free( p );
}
/* Utility functions */
/* Hash a bignum */
static void
bnhash (EVP_MD_CTX *ctx, BIGNUM *bn)
{
unsigned buflen = BN_bn2mpi(bn, NULL);
uchar *buf = malloc (buflen);
BN_bn2mpi (bn, buf);
EVP_DigestUpdate (ctx, buf, buflen);
free (buf);
}
/* Communication functions */
/* Encrypt and send a message */
static void
fwrite_enc (uchar *buf, int len, FILE *f)
{
EVP_CIPHER_CTX encctx;
uchar hbuf[SHA_DIGEST_LENGTH];
uchar iv[16];
unsigned outlen = (len + 16);
uchar *outbuf = malloc(outlen);
unsigned out1, out2;
unsigned out12;
int i;
RAND_bytes (iv, sizeof(iv));
EVP_EncryptInit (&encctx, EVP_aes_128_cbc(), enckey, iv);
EVP_EncryptUpdate (&encctx, outbuf, &out1, buf, len);
EVP_EncryptFinal (&encctx, outbuf+out1, &out2);
HMAC (EVP_sha1(), mymackey, sizeof(mymackey), outbuf, out1+out2,
hbuf, NULL);
/* Increment MAC key every time to get the effect of a seq number */
for (i=sizeof(mymackey)-1; ++mymackey[i]==0; i--)
;
out12 = htonl(out1+out2+sizeof(iv)+SHA_DIGEST_LENGTH);
fwrite (&out12, 1, sizeof(out12), f);
fwrite (iv, 1, sizeof(iv), f);
fwrite (outbuf, 1, out1+out2, f);
fwrite (hbuf, 1, SHA_DIGEST_LENGTH, f);
fflush (f);
free (outbuf);
}
/* Read and decrypt a message */
static void
fread_dec (uchar *buf, int len, FILE *f)
{
EVP_CIPHER_CTX decctx;
uchar hbuf[SHA_DIGEST_LENGTH];
uchar hbuf2[SHA_DIGEST_LENGTH];
uchar iv[16];
unsigned inlen = (len + 16);
uchar *inbuf = malloc(inlen);
uchar *outbuf = malloc (len + 2*16);
unsigned in1, in2;
unsigned in12;
int nread;
int i;
fread (&in12, 1, sizeof(in12), f);
in12 = ntohl(in12);
if (in12-sizeof(iv)-SHA_DIGEST_LENGTH > inlen)
{
fprintf (stderr, "Excessively large input packet %d, protocol failure\n",
in12);
exit (2);
}
fread (iv, 1, sizeof(iv), f);
nread = fread (inbuf, 1, in12-sizeof(iv)-SHA_DIGEST_LENGTH, f);
fread (hbuf, 1, SHA_DIGEST_LENGTH, f);
HMAC (EVP_sha1(), hismackey, sizeof(hismackey), inbuf, nread,
hbuf2, NULL);
if (memcmp (hbuf, hbuf2, sizeof(hbuf)) != 0)
{
fprintf (stderr, "Incorrect message hash, protocol failure or attack\n");
exit (2);
}
/* Increment MAC key every time to get the effect of a seq number */
for (i=sizeof(hismackey)-1; ++hismackey[i]==0; i--)
;
EVP_DecryptInit (&decctx, EVP_aes_128_cbc(), deckey, iv);
EVP_DecryptUpdate (&decctx, outbuf, &in1, inbuf, nread);
EVP_DecryptFinal (&decctx, outbuf+in1, &in2);
if (in1 + in2 != len)
{
fprintf (stderr, "Incorrect incoming message length, protocol failure\n");
exit (2);
}
memcpy (buf, outbuf, in1+in2);
free (inbuf);
free (outbuf);
}
/* Hash a message, include DH parameters */
static void
hashmsg (uchar *hashbuf, uchar *msg, int msglen, int direction, int mine)
{
EVP_MD_CTX hashctx;
uchar dir = (uchar)direction;
EVP_DigestInit (&hashctx, EVP_sha1());
bnhash (&hashctx, shared);
if (mine)
{
bnhash (&hashctx, my_y);
bnhash (&hashctx, his_y);
} else {
bnhash (&hashctx, his_y);
bnhash (&hashctx, my_y);
}
EVP_DigestUpdate (&hashctx, &dir, 1);
EVP_DigestUpdate (&hashctx, msg, msglen);
EVP_DigestFinal (&hashctx, hashbuf, 0);
EVP_MD_CTX_cleanup (&hashctx);
}
/* This is the main loop of the program. Each side reads a message,
* then they exchange hashes of those messages, hashed with the public
* and secret DH handshake values. No, I need to add some randomness there,
* too, don't I?
*/
static void
intercomm (int prio, FILE *f_in, FILE *f_out)
{
char mybuf[PADLEN+MYBUFLEN];
char hisbuf[PADLEN+MYBUFLEN];
uchar myhash[SHA_DIGEST_LENGTH];
uchar hishash[SHA_DIGEST_LENGTH];
uchar hishash2[SHA_DIGEST_LENGTH];
int mylen, mylen2, hislen;
mybuf[sizeof(mybuf)-1] = '\0';
for ( ; ; )
{
/* Prefix message by random bytes to make it unguessable */
RAND_bytes (mybuf, PADLEN);
fgets (mybuf+PADLEN, sizeof(mybuf)-PADLEN-1, stdin);
mylen = strlen(mybuf+PADLEN) + PADLEN;
/* Strip end of line characters */
while ((mybuf[mylen-1] == '\n' || mybuf[mylen-1] == '\r')
&& mylen > PADLEN)
--mylen;
/* Hash message and exchange hashes */
hashmsg (myhash, mybuf, mylen, prio, 1);
if (prio)
{
fwrite_enc (myhash, sizeof(myhash), f_out);
fread_dec (hishash, sizeof(hishash), f_in);
} else {
fread_dec (hishash, sizeof(hishash), f_in);
fwrite_enc (myhash, sizeof(myhash), f_out);
}
/* Exchange messages and verify remote hash */
if (prio)
{
mylen2 = htonl(mylen);
fwrite_enc ((uchar *)&mylen2, sizeof(mylen2), f_out);
fwrite_enc (mybuf, mylen, f_out);
}
fread_dec ((uchar *)&hislen, sizeof(hislen), f_in);
hislen = ntohl(hislen);
if (hislen >= sizeof(hisbuf)-1 || hislen < PADLEN)
{
fprintf (stderr, "Incoming buffer bad size, %d bytes\n", hislen);
exit (2);
}
fread_dec (hisbuf, hislen, f_in);
hisbuf[hislen] = '\0';
hashmsg (hishash2, hisbuf, hislen, !prio, 0);
if (memcmp (hishash, hishash2, sizeof(hishash)) != 0)
{
fprintf (stderr, "Incorrect hash, protocol failure or attack detected\n");
exit (2);
}
printf ("OTHER: %s\n", hisbuf+PADLEN);
if (!prio)
{
mylen2 = htonl(mylen);
fwrite_enc ((uchar *)&mylen2, sizeof(mylen2), f_out);
fwrite_enc (mybuf, mylen, f_out);
}
}
}
/* Setup and handshake functions */
/* Do an anonymous DH exchange */
static int
dodh (int prio, FILE *f_in, FILE *f_out)
{
my_x = BN_new();
my_y = BN_new();
shared = BN_new();
p = BN_new();
q = BN_new();
g = BN_new();
BN_hex2bn( &p, p_hex );
BN_hex2bn( &q, q_hex );
BN_hex2bn( &g, g_hex );
BN_rand (my_x, XBITS, 0, 0);
BN_mod_exp (my_y, g, my_x, p, bnctx);
if (prio)
{
bnwrite (my_y, f_out);
fflush (f_out);
bnread (&his_y, f_in);
} else {
bnread (&his_y, f_in);
bnwrite (my_y, f_out);
fflush (f_out);
}
BN_mod_exp (shared, his_y, my_x, p, bnctx);
}
/* Generate a key of the given length and name, using HMAC-SHA1 */
static void
keygen (uchar *key, unsigned keylen, char *name)
{
int namelen = strlen(name) + 1 + 1;
char *namebuf = malloc (namelen);
uchar hbuf[SHA_DIGEST_LENGTH];
int hbuflen;
int iter = '0';
unsigned sharedlen = BN_bn2mpi(shared, NULL);
uchar *sharedbuf = malloc(sharedlen);
BN_bn2mpi (shared, sharedbuf);
while (keylen)
{
/* put a prefix char on the name for wide keygen */
namebuf[0] = iter++;
strcpy (namebuf+1, name);
HMAC (EVP_sha1(), sharedbuf, sharedlen, namebuf, namelen,
hbuf, NULL);
hbuflen = MIN(keylen, SHA_DIGEST_LENGTH);
memcpy (key, hbuf, hbuflen);
keylen -= hbuflen;
key += hbuflen;
}
memset (sharedbuf, 0, sharedlen);
memset (hbuf, 0, SHA_DIGEST_LENGTH);
free (namebuf);
free (sharedbuf);
}
/* Do the handshake and fall into the communications loop */
static int
docomm (int prio, FILE *f_in, FILE *f_out)
{
dodh (prio, f_in, f_out);
if (prio)
{
keygen (enckey, sizeof(enckey), "Client interlock encrypt");
keygen (deckey, sizeof(deckey), "Server interlock encrypt");
keygen (mymackey, sizeof(mymackey), "Client interlock HMAC");
keygen (hismackey, sizeof(hismackey), "Server interlock HMAC");
} else {
keygen (enckey, sizeof(enckey), "Server interlock encrypt");
keygen (deckey, sizeof(deckey), "Client interlock encrypt");
keygen (mymackey, sizeof(mymackey), "Server interlock HMAC");
keygen (hismackey, sizeof(hismackey), "Client interlock HMAC");
}
intercomm (prio, f_in, f_out);
/* Never returns */
return 0;
}
static int
client (char *target, int port, int nsides)
{
struct hostent *targetinfo;
int s, s1;
int n;
struct sockaddr_in sockaddr;
FILE *f_in, *f_out;
int err;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror ("socket");
exit (2);
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
if (!(targetinfo = gethostbyname(target))) {
fprintf (stderr, "Unknown target machine name\n");
exit (2);
}
if (!targetinfo->h_addr_list) {
fprintf (stderr, "No address information available for %s\n",
target);
exit (2);
}
sockaddr.sin_addr.s_addr = **(u_long **)targetinfo->h_addr_list;
if (connect (s, &sockaddr, sizeof(sockaddr)) < 0) {
perror ("connect");
exit (2);
}
f_in = fdopen (s, "r");
f_out = fdopen (dup(s), "w");
err = docomm (0, f_in, f_out);
fclose (f_in);
fclose (f_out);
return err;
}
static char *
revlookup (struct sockaddr_in *otheraddr)
{
unsigned addr = otheraddr->sin_addr.s_addr;
static char buf[1024];
struct hostent *he = gethostbyaddr ((char *)&addr, sizeof (addr), AF_INET);
if (he == NULL)
{
sprintf (buf, "[%d.%d.%d.%d]", addr&0xff, (addr>>8)&0xff,
(addr>>16)&0xff, (addr>>24)&0xff);
} else {
strncpy (buf, he->h_name, sizeof(buf)-1);
}
return buf;
}
static int
server (int port, int nsides)
{
int s, s1;
int n;
struct sockaddr_in sockaddr, otheraddr;
int otheraddrsize = sizeof(otheraddr);
int reuseflag = -1;
FILE *f_in, *f_out;
int err;
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror ("socket");
exit (2);
}
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &reuseflag, sizeof(reuseflag));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr.s_addr = INADDR_ANY;
if (bind (s, &sockaddr, sizeof(sockaddr)) < 0) {
perror ("bind");
exit (2);
}
if (listen(s, 5) < 0) {
perror ("listen");
exit (2);
}
s1 = accept (s, &otheraddr, &otheraddrsize);
if (s1 < 0) {
perror ("accept");
exit (2);
}
fprintf (stderr, "Incoming connection from %s\n", revlookup(&otheraddr));
close(s);
f_in = fdopen (s1, "r");
f_out = fdopen (dup(s1), "w");
err = docomm (1, f_in, f_out);
fclose (f_in);
fclose (f_out);
return err;
}
int
main (int ac, char **av)
{
int port;
char *host = NULL;
int nsides;
bnctx = BN_CTX_new();
if (ac < 2 || ac > 3)
{
fprintf (stderr, "Usage: %s [otherhost] port\n", av[0]);
exit (1);
}
if (ac == 3)
{
host = av[1];
av[1] = av[0];
av++; ac--;
}
port = atoi(av[1]);
if (port < 0 || port > 65536)
{
fprintf (stderr, "Illegal port number %d\n", port);
exit (1);
}
if (host == NULL)
return server (port, nsides);
else
return client (host, port, nsides);
}
---------------------------------------------------------------------
The Cryptography Mailing List
Unsubscribe by sending "unsubscribe cryptography" to majordomo at metzdowd.com
More information about the cryptography
mailing list