--- configure.bak	2003-10-13 10:11:05.000000000 -0500
+++ configure	2003-10-01 12:34:41.000000000 -0500
@@ -25,8 +25,20 @@
 MANDIR=""
 LIBDIRS=`cat /etc/ld.so.conf 2> /dev/null`
 PREFIX=`echo "$1"|sed 's/.*--prefix=//'`
+LIBDES=""
 echo
 
+echo "Checking for libdes ..."
+for i in $LIBDIRS /lib /usr/lib /usr/local/lib /opt/local/lib
+do
+    if [ "X" = "X$LIBDES" ]; then
+        if [ -f "$i/libdes.a" ]; then
+            LIBDES="$i/libdes.a"
+        fi
+    fi
+done
+
+
 echo "Checking for openssl ..."
 for i in  $LIBDIRS /lib  /usr/lib /usr/local/lib /opt/local/lib \
 /*ssl /usr/*ssl /opt/*ssl /usr/local/*ssl /opt/local/*ssl \
@@ -164,6 +176,7 @@
 echo "XLIBPATHS=$XLIBPATHS" >> Makefile.in
 echo "XIPATHS=$XIPATHS" >> Makefile.in
 echo "PREFIX=$PREFIX" >> Makefile.in
+echo "LIBDES=$LIBDES" >> Makefile.in
 echo >> Makefile.in
 
 echo "Generating Makefile ..."
--- hydra.c.bak	2003-10-13 10:04:32.000000000 -0500
+++ hydra.c	2003-10-13 10:08:38.000000000 -0500
@@ -11,7 +11,7 @@
 
 #include "hydra.h"
 
-extern void service_telnet(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
+extern void service_telnet(unsigned long int ip, int sp, unsigned char options, char *iscptr, FILE *fp, int port);
 extern void service_ftp(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 extern void service_pop3(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 extern void service_imap(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
@@ -26,9 +26,10 @@
 extern void service_icq(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 extern void service_pcnfs(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 extern void service_smb(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
+extern void service_smbnt(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 extern void service_mysql(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port);
 // ADD NEW SERVICES HERE
-#define SERVICES  "telnet ftp pop3 imap smb http https cisco cisco-enable ldap mysql nntp vnc rexec socks5 icq pcnfs"
+#define SERVICES  "telnet ftp pop3 imap smb smbnt http https cisco cisco-enable ldap mysql nntp vnc rexec socks5 icq pcnfs"
 // ADD NEW SERVICES HERE
 
 #define MAXBUF    264
@@ -250,6 +251,13 @@
         }
         i = 1;
     }
+    if (strcmp(service, "smbnt") == 0) {
+        if (tasks > 1) {
+            fprintf(stderr, "Reduced number of tasks to 1 (smb does not like parallel connections)\n");
+            tasks = 1;
+        }
+        i = 1;
+    }
     if (strcmp(service, "pcnfs") == 0) {
         i = 1;
         if (port == 0) {
@@ -472,6 +480,7 @@
     if (strcmp(service, "icq") == 0) service_icq(ip, socketpairs[i][1], options, miscptr, ofp, port);
     if (strcmp(service, "pcnfs") == 0) service_pcnfs(ip, socketpairs[i][1], options, miscptr, ofp, port);
     if (strcmp(service, "smb") == 0) service_smb(ip, socketpairs[i][1], options, miscptr, ofp, port);
+    if (strcmp(service, "smbnt") == 0) service_smbnt(ip, socketpairs[i][1], options, miscptr, ofp, port);
     if (strcmp(service, "mysql") == 0) service_mysql(ip, socketpairs[i][1], options, miscptr, ofp, port);
 // ADD NEW SERVICES HERE
                 return 0;
--- hydra-smbnt.c.bak	2003-10-13 10:09:20.000000000 -0500
+++ hydra-smbnt.c	2003-10-13 10:58:05.000000000 -0500
@@ -0,0 +1,379 @@
+#include "hydra-mod.h"
+#include "md4.h"
+#include <des.h>
+
+/*
+   SMB NTLM Password/HASH Checking Hydra Module
+   Copyright (C) m0j0.j0j0
+   m0j0.j0j0 / m0j0@foofus.net 
+   09/30/2003
+
+   Based on code from: SMB Auditing Tool
+   [Copyright (C) Patrik Karlsson 2001]
+
+   This code allows Hydra to directly test NTLM hashes against
+   a Windows host via '-m HASH'. This may be useful for an 
+   auditor who has aquired a sam._ or pwdump file and would 
+   like to quickly determine which are valid entries. This 
+   module can also be used to test SMB passwords against devices 
+   that do not allow clear text LanMan passwords.
+
+   See http://www.foofus.net/m0j0/passhash.html for further
+   examples.
+
+   Greets: Foofus, Phenfen & Fizzgig  
+*/
+
+extern char *HYDRA_EXIT;
+char challenge[8];
+
+static u_char Get7Bits(u_char *input, int startBit) {
+  register unsigned int word;
+
+  word  = (unsigned)input[startBit / 8] << 8;
+  word |= (unsigned)input[startBit / 8 + 1];
+
+  word >>= 15 - (startBit % 8 + 7);
+
+  return word & 0xFE;
+}
+
+// Make the key
+static void MakeKey(u_char *key, u_char *des_key) {
+  des_key[0] = Get7Bits(key,  0);
+  des_key[1] = Get7Bits(key,  7);
+  des_key[2] = Get7Bits(key, 14);
+  des_key[3] = Get7Bits(key, 21);
+  des_key[4] = Get7Bits(key, 28);
+  des_key[5] = Get7Bits(key, 35);
+  des_key[6] = Get7Bits(key, 42);
+  des_key[7] = Get7Bits(key, 49);
+
+  des_set_odd_parity((des_cblock *)des_key);
+}
+
+// Do the DesEncryption
+void DesEncrypt(u_char *clear, u_char *key, u_char *cipher) {
+  des_cblock         des_key;
+  des_key_schedule   key_schedule;
+
+  MakeKey(key, des_key);
+  des_set_key(&des_key, key_schedule);
+  des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+}
+
+/*
+  HashNTLM
+  Function: Create a NTLM hash from the challenge
+  Variables:
+        ntlmhash  = the hash created from this function
+        pass      = users password
+        challenge = the challenge recieved from the server
+*/
+void HashNTLM(u_char **ntlmhash, u_char *pass, u_char *challenge, char *miscptr) {
+  MD4_CTX             md4Context;
+  u_char              hash[16];                   // MD4_SIGNATURE_SIZE = 16
+  u_char              unicodePassword[256 * 2];   // MAX_NT_PASSWORD = 256
+  u_char              p21[21];
+  u_char              ntlm_response[24];
+  int                 i=0,j=0;
+  int                 mdlen = strlen(pass) * 2 * 8;
+  u_char              *p;
+  
+  /* Use NTLM Hash instead of password */
+  if ((miscptr != NULL) && (strcmp(miscptr, "HASH") == 0)) { 
+    // 1000:D42E35E1A1E4C22BD32E2170E4857C20:5E20780DD45857A68402938C7629D3B2:::
+    p = pass;
+    while ((*p != '\0') && (i < 2)) {
+      if (*p == ':') i++;
+      p++;
+    }
+
+    if (*p == '\0') {
+      fprintf(stderr, "Error reading PWDUMP file.\n");
+      exit(1);
+    }
+
+    char HexChar;
+    int HexValue;
+    for (i=0; i<16; i++) {
+      HexValue = 0x0;
+      for (j=0; j<2; j++) {
+        HexChar = (char)p[2*i+j];
+
+        if (HexChar > 0x39)
+          HexChar = HexChar | 0x20;  /* convert upper case to lower */
+
+        if (!(((HexChar >= 0x30) && (HexChar <= 0x39))||   /* 0 - 9 */
+              ((HexChar >= 0x61) && (HexChar <= 0x66)))) {    /* a - f */
+          //fprintf(stderr, "Error invalid char (%c) for hash.\n", HexChar);
+          //exit(1);
+          HexChar = 0x30;
+        }
+
+        HexChar -= 0x30;
+        if (HexChar > 0x09)  /* HexChar is "a" - "f" */
+          HexChar -= 0x27;
+
+        HexValue = (HexValue << 4) | (char)HexChar;
+      }
+      hash[i] = (u_char)HexValue;
+    }
+  } else {
+    /* Initialize the Unicode version of the secret (== password). */
+    /* This implicitly supports 8-bit ISO8859/1 characters. */
+    bzero(unicodePassword, sizeof(unicodePassword));
+    for (i = 0; i < strlen(pass); i++)
+      unicodePassword[i * 2] = (u_char)pass[i];
+
+    MD4Init(&md4Context);
+    MD4Update(&md4Context, unicodePassword, mdlen);
+    MD4Final(hash, &md4Context);        /* Tell MD4 we're done */
+  }
+
+  memset(p21, '\0', 21);
+  memcpy(p21, hash, 16);
+
+  DesEncrypt(challenge, p21 +  0, ntlm_response + 0);
+  DesEncrypt(challenge, p21 +  7, ntlm_response + 8);
+  DesEncrypt(challenge, p21 + 14, ntlm_response + 16);
+                                                                                                                                                
+  memcpy(*ntlmhash, ntlm_response, 24);
+}
+
+/*
+   NBS Session Request
+   Function: Request a new session from the server
+   Returns: TRUE on success else FALSE.
+*/
+int NBSSessionRequest(int s) {
+  char nb_name[32];      // netbiosname
+  char nb_local[32];     // netbios localredirector
+  char rqbuf[7] = {0x81, 0x00, 0x00, 0x48, 0x20, 0x00, 0x20};
+  char *buf;
+  u_char rbuf[400];
+
+  // convert computer name to netbios name
+  memset(nb_name, 0, 32);
+  memset(nb_local, 0, 32);
+  memcpy(nb_name, "CKFDENECFDEFFCFGEFFCCACACACACACA", 32); // *SMBSERVER
+  memcpy(nb_local, "EIFJEEFCEBCACACACACACACACACACACA", 32); // HYDRA
+
+  buf = (char *) malloc(100);
+  memset(buf, 0, 100);
+  memcpy(buf, rqbuf, 5);
+  memcpy(buf+5, nb_name, 32);
+  memcpy(buf+37, rqbuf+5, 2);
+  memcpy(buf+39, nb_local, 32);
+  memcpy(buf+71, rqbuf+5, 1);
+
+  hydra_send(s, buf, 72, 0);
+  free(buf);
+
+  memset(rbuf, 0, 400);
+  hydra_recv(s, rbuf, sizeof(rbuf)); 
+
+  if (rbuf[0] == 0x82)
+    return 0;	    /* success */
+  else
+    return -1;     /* failed */
+}
+
+
+/*
+   SMBNegProt
+   Function: Negotiate protocol with server ...
+       Actually a pseudo negotiation since the whole
+       program counts on NTLM support :)
+
+    The challenge is retrieved from the answer
+    No error checking is performed i.e cross your fingers....
+*/
+int SMBNegProt(int s) {
+  char buf[168] = {0x00, 0x00, 0x00, 0xa4, 0xff, 0x53, 0x4d, 0x42,
+      0x72, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x40,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x7d,
+      0x00, 0x00, 0x01, 0x00, 0x00, 0x81, 0x00, 0x02,
+      0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
+      0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52,
+      0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02,
+      0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46,
+      0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52,
+      0x4b, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x00,
+      0x02, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f,
+      0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
+      0x52, 0x4b, 0x53, 0x20, 0x33, 0x2e, 0x30, 0x00,
+      0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31,
+      0x2e, 0x30, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e,
+      0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53,
+      0x61, 0x6d, 0x62, 0x61, 0x00, 0x02, 0x4e, 0x54,
+      0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x20,
+      0x31, 0x2e, 0x30, 0x00, 0x02, 0x4e, 0x54, 0x20,
+      0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00};
+
+  char rbuf[400];
+  memset(rbuf, 0, 400);
+
+  hydra_send(s, buf, 168, 0);
+  hydra_recv(s, rbuf, sizeof(rbuf)); 
+
+  // retrieve the challenge
+  memcpy(challenge, rbuf+73, sizeof(challenge));
+
+  return 2;
+}
+
+
+/*
+  SMBSessionSetup
+  Function: Send username + response to the challenge from
+            the server.
+       Currently we're sendin ZEROES for the LMHASH since
+       NT4/2000 doesn't seem to look at this if we got a
+       valid NTLM hash.
+  Returns: TRUE on success else FALSE.
+*/
+int SMBSessionSetup(int s, char *user, char *pass, char *miscptr) {
+  char b[137] = {0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d,
+        0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01,
+        0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+        0x7d, 0x00, 0x00, 0x01, 0x00, 0x0d, 0xff, 0x00,
+        0x00, 0x00, 0xff, 0xff, 0x02, 0x00, 0x3c, 0x7d,
+        0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+        0x48, 0x00, 0xdf, 0x82, 0xb9, 0x2f, 0xda, 0xdb,
+        0x43, 0x47, 0x09, 0xdb, 0x7f, 0xfc, 0xb0, 0xa8,
+        0xf0, 0x46, 0xe2, 0xfe, 0x64, 0x6d, 0x67, 0x58,
+        0xe0, 0xf9, 0xca, 0x9f, 0x5e, 0xdb, 0xe0, 0x15,
+        0x80, 0xf8, 0x54, 0xe3, 0x6e, 0xe9, 0xf8, 0x88,
+        0x80, 0x19, 0xb6, 0xf9, 0xae, 0xb7, 0xa6, 0x62,
+        0x14, 0xfc, 0x54, 0x45, 0x53, 0x54, 0x00, 0x4d,
+        0x59, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x00, 0x55,
+        0x6e, 0x69, 0x78, 0x00, 0x53, 0x61, 0x6d, 0x62,
+        0x61, 0x00};
+
+  u_char buf[512];
+  u_char *NTLMhash;
+  u_char LMhash[24];
+  u_char workgroup[16];
+  u_char rbuf[400];
+  int userlen;
+
+  NTLMhash   = (u_char *) malloc(24);
+
+  memset(NTLMhash, 0, 24);
+  memset(LMhash, 0, 24);
+  memset(workgroup, 0, 16);
+  memset(rbuf, 0, 400);
+
+  HashNTLM(&NTLMhash, pass, challenge, miscptr);
+
+  memset(buf, 0, 512);
+  memcpy(buf, b, 89);
+  memcpy(buf+65, LMhash, 24);
+  memcpy(buf+89, NTLMhash, 24);
+
+  userlen = strlen(user);
+
+  memcpy(buf+113, user, userlen);
+  memset(buf+(113+userlen), 0, 1);
+  memcpy(buf+(114+userlen), workgroup, strlen(workgroup));
+  memcpy(buf+(114+userlen+strlen(workgroup)), b+125, 12);
+
+  // set the header length
+  buf[3] = ( userlen + strlen(workgroup) + 0x7A ) % 256;
+  buf[2] = ( userlen + strlen(workgroup) + 0x7A ) / 256;
+
+  // set data length
+  buf[63] = 0x1F + strlen(workgroup) + userlen;
+
+  hydra_send(s, buf, 126+userlen+strlen(workgroup), 0);
+
+  hydra_recv(s, rbuf, sizeof(rbuf));
+  return rbuf[9];
+}
+
+int start_smbnt(int s, int port, unsigned char options, char *miscptr, FILE *fp) {
+  char *empty = "";
+  char *login, *pass;
+
+  if (strlen(login = hydra_get_next_login()) == 0) login = empty;
+  if (strlen(pass = hydra_get_next_password()) == 0) pass = empty;
+
+  int SMBerr = SMBSessionSetup(s, login, pass, miscptr);
+
+  if ( 0x00 == SMBerr ) {                  // success
+    hydra_report_found(port, "smb", fp);
+    hydra_completed_pair_found();
+  }
+  else if ( 0x24 == SMBerr ) {             // change password on next login [success]
+    hydra_report_found(port, "smb (must change password)", fp);
+    hydra_completed_pair_found();
+  }
+  else if ( 0x72 == SMBerr ) {             // account disabled
+    hydra_report_found(port, "smb (account disabled)", fp);
+    hydra_completed_pair();
+  }
+  else if ( 0x34 == SMBerr ) {             // account locked out
+    fprintf(stderr, "[%d][smb] Account: %s is locked out\n", port, login); 
+    hydra_completed_pair();
+  }
+  else {                                   // failed
+    hydra_completed_pair();
+  }
+
+  hydra_disconnect(s);
+  if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
+    return 3;
+  return 1;
+}
+
+void service_smbnt(unsigned long int ip, int sp, unsigned char options, char *miscptr, FILE *fp, int port) {
+  int run = 1, next_run, sock = -1;
+  int myport = PORT_SMB, mysslport = PORT_SMB_SSL;
+
+  hydra_register_socket(sp);
+  if (memcmp(hydra_get_next_pair(), &HYDRA_EXIT, sizeof(HYDRA_EXIT)) == 0)
+    return;
+  for(;;) {
+    switch(run) {
+      case 1: /* connect and service init function */
+          if (sock >= 0)
+            sock = hydra_disconnect(sock);
+            usleep(300000);
+          if ((options & OPTION_SSL) == 0) {
+            if (port != 0) myport = port;
+            sock = hydra_connect_tcp(ip, myport);
+            port = myport;
+          } else {
+            if (port != 0) mysslport = port;
+            sock = hydra_connect_ssl(ip, mysslport);
+            port = mysslport;
+          }
+          if (sock < 0) {
+            fprintf(stderr, "Error: Child with pid %d terminating, can not connect\n", (int)getpid());
+            hydra_child_exit();
+          }
+          if ( NBSSessionRequest(sock) < 0 ) {
+            fprintf(stderr, "Session Setup Failed: ");
+            fprintf(stderr, "(Is the server service running?)\n");
+            exit(-1);
+          }
+          next_run = SMBNegProt(sock);
+          break;
+      case 2: /* run the cracking function */
+          next_run = start_smbnt(sock, port, options, miscptr, fp);
+          break;
+      case 3: /* clean exit */
+          if (sock >= 0) sock = hydra_disconnect(sock);
+          hydra_child_exit();
+          return;
+      default: fprintf(stderr,"Caught unknown return code (%d), exiting!\n", run);
+           hydra_child_exit();
+           exit(-1);
+    }
+    run = next_run;
+  }
+}
--- Makefile.am.bak	2003-10-13 10:10:16.000000000 -0500
+++ Makefile.am	2003-10-01 12:34:41.000000000 -0500
@@ -4,26 +4,26 @@
 
 CC = gcc
 OPTS = -O2 -I. -Wall
-LIBS = 
+LIBS =
 DIR = /bin
 
 SRC = hydra-vnc.c hydra-pcnfs.c hydra-rexec.c hydra-nntp.c hydra-socks5.c \
       hydra-telnet.c hydra-cisco.c hydra-http.c hydra-ftp.c hydra-imap.c \
       hydra-pop3.c hydra-smb.c hydra-icq.c hydra-cisco-enable.c hydra-ldap.c \
-      hydra-mysql.c d3des.c hydra-mod.c hydra.c
+      hydra-mysql.c hydra-smbnt.c d3des.c md4.c hydra-mod.c hydra.c
 OBJ = hydra-vnc.o hydra-pcnfs.o hydra-rexec.o hydra-nntp.o hydra-socks5.o \
       hydra-telnet.o hydra-cisco.o hydra-http.o hydra-ftp.o hydra-imap.o \
       hydra-pop3.o hydra-smb.o hydra-icq.o hydra-cisco-enable.o hydra-ldap.o \
-      hydra-mysql.o d3des.o hydra-mod.o hydra.o
+      hydra-mysql.o hydra-smbnt.o d3des.o md4.o hydra-mod.o hydra.o
 BIN = hydra
 
 EXTRA_DIST = README CHANGES TODO INSTALL LICENSE.GNU LICENCE.HYDRA \
-             hydra-mod.h hydra.h d3des.h
+             hydra-mod.h hydra.h d3des.h md4.h
 
 all:	hydra
 
 hydra:	$(OBJ)
-	$(CC) $(OPTS) $(LIBS) -o $(BIN) $(OBJ) $(LIB) $(XLIBS) $(XLIBPATHS)
+	$(CC) $(OPTS) $(LIBS) -o $(BIN) $(OBJ) $(LIB) $(XLIBS) $(XLIBPATHS) $(LIBDES)
 	@echo
 	@echo If men could get pregnant, abortion would be a sacrament
 	@echo
--- md4.c.bak	2003-10-13 10:09:31.000000000 -0500
+++ md4.c	2003-10-01 12:34:41.000000000 -0500
@@ -0,0 +1,297 @@
+/*
+** ********************************************************************
+** md4.c -- Implementation of MD4 Message Digest Algorithm           **
+** Updated: 2/16/90 by Ronald L. Rivest                              **
+** (C) 1990 RSA Data Security, Inc.                                  **
+** ********************************************************************
+*/
+
+/*
+** To use MD4:
+**   -- Include md4.h in your program
+**   -- Declare an MDstruct MD to hold the state of the digest
+**          computation.
+**   -- Initialize MD using MDbegin(&MD)
+**   -- For each full block (64 bytes) X you wish to process, call
+**          MD4Update(&MD,X,512)
+**      (512 is the number of bits in a full block.)
+**   -- For the last block (less than 64 bytes) you wish to process,
+**          MD4Update(&MD,X,n)
+**      where n is the number of bits in the partial block. A partial
+**      block terminates the computation, so every MD computation
+**      should terminate by processing a partial block, even if it
+**      has n = 0.
+**   -- The message digest is available in MD.buffer[0] ...
+**      MD.buffer[3].  (Least-significant byte of each word
+**      should be output first.)
+**   -- You can print out the digest using MDprint(&MD)
+*/
+
+/* Implementation notes:
+** This implementation assumes that ints are 32-bit quantities.
+*/
+
+#define TRUE  1
+#define FALSE 0
+
+/* Compile-time includes
+*/
+#include <stdio.h>
+#include "md4.h"
+
+/* Compile-time declarations of MD4 "magic constants".
+*/
+#define I0  0x67452301       /* Initial values for MD buffer */
+#define I1  0xefcdab89
+#define I2  0x98badcfe
+#define I3  0x10325476
+#define C2  013240474631     /* round 2 constant = sqrt(2) in octal */
+#define C3  015666365641     /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+** Table 2, page 660.
+*/
+
+#define fs1  3               /* round 1 shift amounts */
+#define fs2  7
+#define fs3 11
+#define fs4 19
+#define gs1  3               /* round 2 shift amounts */
+#define gs2  5
+#define gs3  9
+#define gs4 13
+#define hs1  3               /* round 3 shift amounts */
+#define hs2  9
+#define hs3 11
+#define hs4 15
+
+/* Compile-time macro declarations for MD4.
+** Note: The "rot" operator uses the variable "tmp".
+** It assumes tmp is declared as unsigned int, so that the >>
+** operator will shift in zeros rather than extending the sign bit.
+*/
+#define f(X,Y,Z)             ((X&Y) | ((~X)&Z))
+#define g(X,Y,Z)             ((X&Y) | (X&Z) | (Y&Z))
+#define h(X,Y,Z)             (X^Y^Z)
+#define rot(X,S)             (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s)      A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s)      A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s)      A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+/* MD4print(MDp)
+** Print message digest buffer MDp as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte of
+** buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+** This is a user-callable routine.
+*/
+void
+MD4Print(MDp)
+MD4_CTX *MDp;
+{
+  int i,j;
+  for (i=0;i<4;i++)
+    for (j=0;j<32;j=j+8)
+      printf("%02x",(MDp->buffer[i]>>j) & 0xFF);
+}
+
+/* MD4Init(MDp)
+** Initialize message digest buffer MDp.
+** This is a user-callable routine.
+*/
+void
+MD4Init(MDp)
+MD4_CTX *MDp;
+{
+  int i;
+  MDp->buffer[0] = I0;
+  MDp->buffer[1] = I1;
+  MDp->buffer[2] = I2;
+  MDp->buffer[3] = I3;
+  for (i=0;i<8;i++) MDp->count[i] = 0;
+  MDp->done = 0;
+}
+
+/* MDblock(MDp,X)
+** Update message digest buffer MDp->buffer using 16-word data block X.
+** Assumes all 16 words of X are full of data.
+** Does not update MDp->count.
+** This routine is not user-callable.
+*/
+static void
+MDblock(MDp,Xb)
+MD4_CTX *MDp;
+unsigned char *Xb;
+{
+  register unsigned int tmp, A, B, C, D;
+  unsigned int X[16];
+  int i;
+
+  for (i = 0; i < 16; ++i) {
+    X[i] = Xb[0] + (Xb[1] << 8) + (Xb[2] << 16) + (Xb[3] << 24);
+    Xb += 4;
+  }
+
+  A = MDp->buffer[0];
+  B = MDp->buffer[1];
+  C = MDp->buffer[2];
+  D = MDp->buffer[3];
+  /* Update the message digest buffer */
+  ff(A , B , C , D ,  0 , fs1); /* Round 1 */
+  ff(D , A , B , C ,  1 , fs2);
+  ff(C , D , A , B ,  2 , fs3);
+  ff(B , C , D , A ,  3 , fs4);
+  ff(A , B , C , D ,  4 , fs1);
+  ff(D , A , B , C ,  5 , fs2);
+  ff(C , D , A , B ,  6 , fs3);
+  ff(B , C , D , A ,  7 , fs4);
+  ff(A , B , C , D ,  8 , fs1);
+  ff(D , A , B , C ,  9 , fs2);
+  ff(C , D , A , B , 10 , fs3);
+  ff(B , C , D , A , 11 , fs4);
+  ff(A , B , C , D , 12 , fs1);
+  ff(D , A , B , C , 13 , fs2);
+  ff(C , D , A , B , 14 , fs3);
+  ff(B , C , D , A , 15 , fs4);
+  gg(A , B , C , D ,  0 , gs1); /* Round 2 */
+  gg(D , A , B , C ,  4 , gs2);
+  gg(C , D , A , B ,  8 , gs3);
+  gg(B , C , D , A , 12 , gs4);
+  gg(A , B , C , D ,  1 , gs1);
+  gg(D , A , B , C ,  5 , gs2);
+  gg(C , D , A , B ,  9 , gs3);
+  gg(B , C , D , A , 13 , gs4);
+  gg(A , B , C , D ,  2 , gs1);
+  gg(D , A , B , C ,  6 , gs2);
+  gg(C , D , A , B , 10 , gs3);
+  gg(B , C , D , A , 14 , gs4);
+  gg(A , B , C , D ,  3 , gs1);
+  gg(D , A , B , C ,  7 , gs2);
+  gg(C , D , A , B , 11 , gs3);
+  gg(B , C , D , A , 15 , gs4);
+  hh(A , B , C , D ,  0 , hs1); /* Round 3 */
+  hh(D , A , B , C ,  8 , hs2);
+  hh(C , D , A , B ,  4 , hs3);
+  hh(B , C , D , A , 12 , hs4);
+  hh(A , B , C , D ,  2 , hs1);
+  hh(D , A , B , C , 10 , hs2);
+  hh(C , D , A , B ,  6 , hs3);
+  hh(B , C , D , A , 14 , hs4);
+  hh(A , B , C , D ,  1 , hs1);
+  hh(D , A , B , C ,  9 , hs2);
+  hh(C , D , A , B ,  5 , hs3);
+  hh(B , C , D , A , 13 , hs4);
+  hh(A , B , C , D ,  3 , hs1);
+  hh(D , A , B , C , 11 , hs2);
+  hh(C , D , A , B ,  7 , hs3);
+  hh(B , C , D , A , 15 , hs4);
+  MDp->buffer[0] += A;
+  MDp->buffer[1] += B;
+  MDp->buffer[2] += C;
+  MDp->buffer[3] += D;
+}
+
+/* MD4Update(MDp,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+**        count -- the number of bits of X to use.
+**          (if not a multiple of 8, uses high bits of last byte.)
+** Update MDp using the number of bits of X given by count.
+** This is the basic input routine for an MD4 user.
+** The routine completes the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512.  A call with count 0 will be ignored if the
+** MD has already been terminated (done != 0), so an extra call with
+** count 0 can be given as a "courtesy close" to force termination
+** if desired.
+*/
+void
+MD4Update(MDp,X,count)
+MD4_CTX *MDp;
+unsigned char *X;
+unsigned int count;
+{
+  unsigned int i, tmp, bit, byte, mask;
+  unsigned char XX[64];
+  unsigned char *p;
+
+  /* return with no error if this is a courtesy close with count
+  ** zero and MDp->done is true.
+  */
+  if (count == 0 && MDp->done) return;
+  /* check to see if MD is already done and report error */
+  if (MDp->done)
+  { printf("\nError: MD4Update MD already done."); return; }
+
+  /* Add count to MDp->count */
+  tmp = count;
+  p = MDp->count;
+  while (tmp)
+  { tmp += *p;
+  *p++ = tmp;
+  tmp = tmp >> 8;
+  }
+
+  /* Process data */
+  if (count == 512)
+  { /* Full block of data to handle */
+    MDblock(MDp,X);
+  }
+  else if (count > 512) /* Check for count too large */
+  {
+    printf("\nError: MD4Update called with illegal count value %d.",
+	   count);
+    return;
+  }
+  else /* partial block -- must be last block so finish up */
+  {
+    /* Find out how many bytes and residual bits there are */
+    byte = count >> 3;
+    bit =  count & 7;
+    /* Copy X into XX since we need to modify it */
+    for (i=0;i<=byte;i++)   XX[i] = X[i];
+    for (i=byte+1;i<64;i++) XX[i] = 0;
+    /* Add padding '1' bit and low-order zeros in last byte */
+    mask = 1 << (7 - bit);
+    XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+    /* If room for bit count, finish up with this block */
+    if (byte <= 55)
+    {
+      for (i=0;i<8;i++) XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    else /* need to do two blocks to finish up */
+    {
+      MDblock(MDp,XX);
+      for (i=0;i<56;i++) XX[i] = 0;
+      for (i=0;i<8;i++)  XX[56+i] = MDp->count[i];
+      MDblock(MDp,XX);
+    }
+    /* Set flag saying we're done with MD computation */
+    MDp->done = 1;
+  }
+}
+
+/*
+** Finish up MD4 computation and return message digest.
+*/
+void
+MD4Final(buf, MD)
+unsigned char *buf;
+MD4_CTX *MD;
+{
+  int i, j;
+  unsigned int w;
+
+  MD4Update(MD, NULL, 0);
+  for (i = 0; i < 4; ++i) {
+    w = MD->buffer[i];
+    for (j = 0; j < 4; ++j) {
+      *buf++ = w;
+      w >>= 8;
+    }
+  }
+}
+
+/*
+** End of md4.c
+****************************(cut)***********************************/
--- md4.h.bak	2003-10-13 10:09:34.000000000 -0500
+++ md4.h	2003-10-01 12:34:41.000000000 -0500
@@ -0,0 +1,64 @@
+
+/*
+** ********************************************************************
+** md4.h -- Header file for implementation of                        **
+** MD4 Message Digest Algorithm                                      **
+** Updated: 2/13/90 by Ronald L. Rivest                              **
+** (C) 1990 RSA Data Security, Inc.                                  **
+** ********************************************************************
+*/
+
+#ifndef __P
+# if defined(__STDC__) || defined(__GNUC__)
+#  define __P(x) x
+# else
+#  define __P(x) ()
+# endif
+#endif
+
+
+/* MDstruct is the data structure for a message digest computation.
+*/
+typedef struct {
+	unsigned int buffer[4]; /* Holds 4-word result of MD computation */
+	unsigned char count[8]; /* Number of bits processed so far */
+	unsigned int done;      /* Nonzero means MD computation finished */
+} MD4_CTX;
+
+/* MD4Init(MD4_CTX *)
+** Initialize the MD4_CTX prepatory to doing a message digest
+** computation.
+*/
+extern void MD4Init __P((MD4_CTX *MD));
+
+/* MD4Update(MD,X,count)
+** Input: X -- a pointer to an array of unsigned characters.
+**        count -- the number of bits of X to use (an unsigned int).
+** Updates MD using the first "count" bits of X.
+** The array pointed to by X is not modified.
+** If count is not a multiple of 8, MD4Update uses high bits of
+** last byte.
+** This is the basic input routine for a user.
+** The routine terminates the MD computation when count < 512, so
+** every MD computation should end with one call to MD4Update with a
+** count less than 512.  Zero is OK for a count.
+*/
+extern void MD4Update __P((MD4_CTX *MD, unsigned char *X, unsigned int count));
+
+/* MD4Print(MD)
+** Prints message digest buffer MD as 32 hexadecimal digits.
+** Order is from low-order byte of buffer[0] to high-order byte
+** of buffer[3].
+** Each byte is printed with high-order hexadecimal digit first.
+*/
+extern void MD4Print __P((MD4_CTX *));
+
+/* MD4Final(buf, MD)
+** Returns message digest from MD and terminates the message
+** digest computation.
+*/
+extern void MD4Final __P((unsigned char *, MD4_CTX *));
+
+/*
+** End of md4.h
+****************************(cut)***********************************/
