/* ARCFour - a symmetric streaming cipher - Implimentation by Humble * * This is a variable-key-length symmetric stream cipher developed in 1987 * by Ron Rivest (the R in RSA). It used to be proprietary but was reverse * engineered and released publicly in September 1994. The cipher is now * freely available but the name RC4 is a trademark of RSA Data Security * Inc. This cipher is secure, proven, easy to impliment, and quite fast. */ #define IN_MODULE #include "irc.h" #include "struct.h" #include "ircaux.h" #include "ctcp.h" #include "status.h" #include "lastlog.h" #include "server.h" #include "screen.h" #include "vars.h" #include "misc.h" #include "output.h" #include "module.h" #include "hash2.h" #include "hook.h" #include "dcc.h" #include #include #include #include #include "arcfour.h" #include "md5.h" #define INIT_MODULE #include "modval.h" #define MODULE_NAME "arcfour" typedef struct { int sock; char ukey[16]; arckey *outbox; arckey *inbox; } arclist; /* from dcc.c. Why isnt this in dcc.h? */ typedef struct _DCC_List { struct _DCC_List *next; char *nick; /* do NOT free this. it's a shared pointer. */ SocketList sock; } DCC_List; static arclist *keyboxes[16]; static const double arc_ver = 1.0; static unsigned int typenum = 0; /* * Initialize our box, and return the key used. It better be enough to hold * 16 chars + 1 null! */ static char *init_box(char *ukey, arckey *key) { MD5_CTX md5context; unsigned char buf[256]; int fd; fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { read(fd, buf, sizeof(buf)); close(fd); } else return NULL; MD5Init(&md5context); MD5Update(&md5context, buf, sizeof(buf)); MD5Final(&md5context); memcpy(ukey, buf, 16); ukey[16] = '\0'; arcfourInit(key, md5context.digest, 16); return ukey; } static inline void arcfourInit(arckey *arc, void *userkey, unsigned short len) { register arcword *S = arc->state, x = 0, y = 0, pos = 0, tmp; unsigned char *userkey_byte = userkey; /* Seed the S-box linearly, then mix in the key while stiring briskly */ arc->i = arc->j = 0; /* Initialize i and j to 0 */ while(--x, (S[x] = x)); /* Initialize S-box, backwards */ /* Note: Some of these optimizations REQUIRE arcword to be 8-bit unsigned */ do { /* Spread user key into real key */ y += S[x] + userkey_byte[pos]; /* Keys, shaken, not stirred */ tmp = S[x]; S[x] = S[y]; S[y] = tmp; /* Swap S[i] and S[j] */ if (++pos >= len) pos = 0; /* Repeat user key to fill array */ } while(++x); /* ++x is faster than x++ */ } static inline char *arcfourCrypt(arckey *arc, char *data, int len) { register arcword *S = arc->state, i = arc->i, j = arc->j, tmp; register int c = 0; do { j += S[++i]; /* Shake S-box, stir well */ tmp = S[i]; S[i] = S[j]; S[j] = tmp; /* Swap S[i] and S[j] */ data[c] ^= S[(S[i] + S[j])]; /* XOR the crypto into our data */ } while (++c < len); /* Neat, ++x is faster then x++ */ arc->i = i; /* Save modified i and j counters */ arc->j = j; /* Continue where we left off */ return data; /* Return pointer to ciphertext */ } int Arcfour_Init(IrcCommandDll **intp, Function_ptr *global_table) { static const struct dcc_ops schat_ops = { NULL, start_dcc_crypt, dcc_schat_input, send_dcc_encrypt, end_dcc_crypt }; initialize_module(MODULE_NAME); memset(keyboxes, 0, sizeof(keyboxes)); typenum = add_dcc_bind("SCHAT", MODULE_NAME, &schat_ops); add_module_proc(DCC_PROC, MODULE_NAME, "SCHAT", "Secure DCC Chat", 0, 0, dcc_sdcc, NULL); return 0; } static arclist *find_box(int sock) { int i, tmp; tmp = sizeof(keyboxes)/sizeof(arclist *); for (i = 0; i < tmp; i++) if (keyboxes[i] && (keyboxes[i]->sock == sock)) return keyboxes[i]; return (arclist *)NULL; } static char *dcc_crypt (int sock, char *buf, int len) { arclist *box; if ((box = find_box(sock))) { arcfourCrypt(box->outbox, buf, len-2); return buf; } return NULL; } static int send_dcc_encrypt (int type, int sock, char *buf, int len) { if (type == DCC_CHAT) { if (dcc_crypt(sock, buf, len)) { write(sock, buf, len); return 0; } } return -1; } static int dcc_schat_input(int type, int sock, char *buf, int parm, int buf_size) { int len; len = dgets(buf, sock, parm, buf_size - 1, NULL); if (len > 0) { buf[len-1] = '\0'; dcc_crypt(sock, buf, len); buf[len] = '\0'; } return len; } /* Here we initialize the cryptography. Send the other end our key, and read * in theirs. The socket "s" won't have a crypt box unless it is supposed to * an encrypted connection. */ static int start_dcc_crypt (int s, int type, unsigned long d_addr, unsigned short d_port) { arclist *tmpbox; put_it("start_dcc_crypt"); if ((tmpbox = find_box(s))) { int len; char randkey[17]; char buf[BIG_BUFFER_SIZE+1]; memset(randkey, 0, sizeof(randkey)); memset(buf, 0, sizeof(buf)); tmpbox->outbox = (arckey *)new_malloc(sizeof(arckey)); if (init_box(randkey, tmpbox->outbox) == NULL) { new_free(&tmpbox->outbox); return -1; } snprintf(buf, BIG_BUFFER_SIZE, "SecureDCC %s\r\n%n", randkey, &len); write(s, buf, len); memset(buf, 0, sizeof(buf)); if ((len = dgets(buf, s, 1, BIG_BUFFER_SIZE, NULL)) > 0) { if (!my_strnicmp("SecureDCC", buf, 9)) { tmpbox->inbox = (arckey *)new_malloc(sizeof(arckey)); arcfourInit(tmpbox->inbox, next_arg(buf, NULL), 16); } } return 0; } return -1; } static int end_dcc_crypt(int s, unsigned long d_addr, unsigned short d_port) { int i; for(i = 0; i < 16; i++) { if (keyboxes[i] && (keyboxes[i]->sock == s)) { new_free(&(keyboxes[i]->inbox)); new_free(&(keyboxes[i]->outbox)); new_free(&keyboxes[i]); return 0; } } return -1; } static void start_dcc_chat(int s) { struct sockaddr_in remaddr; socklen_t sra; int type; int new_s = -1; char *nick = NULL; unsigned long flags; DCC_int *n = NULL; SocketList *sa; sa = get_socket(s); flags = sa->flags; nick = sa->server; sra = sizeof(struct sockaddr_in); new_s = accept(s, (struct sockaddr *) &remaddr, &sra); type = flags & DCC_TYPES; n = get_socketinfo(s); /* This uses the ordinary dcc_chat_socketread() function - it will call our * input filter function dcc_schat_input(). */ if ((add_socketread(new_s, ntohs(remaddr.sin_port), flags, nick, dcc_chat_socketread, NULL)) < 0) { erase_dcc_info(s, 0, "%s", convert_output_format("$G %RDCC error: accept() failed. punt!!", NULL, NULL)); close_socketread(s); return; } flags &= ~DCC_WAIT; flags |= DCC_ACTIVE; set_socketflags(new_s, flags); set_socketinfo(new_s, n); /* put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), "%s %s %s %s %s %d", update_clock(GET_TIME), dcc_types[type], nick, n->userhost?n->userhost:"u@h", inet_ntoa(remaddr.sin_addr),ntohs(remaddr.sin_port))); */ get_time(&n->starttime); close_socketread(s); start_dcc_crypt(new_s, type, n->remote.s_addr, ntohs(remaddr.sin_port)); } /* set up the dcc hooks */ void dcc_sdcc (char *name, char *args) { char *p; if (!my_stricmp(name, "schat") && (strlen(args) > 0)) { if (*args == ' ') new_next_arg(args, &args); else { p = strchr(args, ' '); if (p && *p) *p = 0; } dcc_create(args, "SCHAT", NULL, 0, 0, typenum, DCC_TWOCLIENTS, start_dcc_chat); } }