-------[  Phrack Magazine --- Vol. 9 | Issue 55 --- 09.09.99 --- 11 of 19  ]


-------------------------[  Stego Hasho  ]


--------[  Conehead  ]


----[  Introduction


The use of hash (checksum) functions in a design for encryption/decryption
systems is not export controlled by the U.S. government.  But even if hash
functions aren't allowed to be exported for confidentiality purposes at some
point in the future, there will still be a hidden way of accomplishing privacy
in their approved, exportable forms (unless the export of MACs also becomes
controlled).


----[  Integrity

The common use for a hash function (basically a one-way encryptor as opposed
to a two-way such as DES or RSA, taking a variable sized message and reducing
it to a set number of random bits) is to assure the integrity of a message
from sender to receiver (or anyone else for that matter).  The message and
its sender computed hash are sent across the network where the receiver
compares the received hash with the receiver computed hash using the shared
hash function against the received message.  If there's no match in the hashes,
he/she can assume the message is faulty.

1: H(message)---message,hash--->H(message)


----[  Authentication

While this provides for message integrity, it doesn't provide message
authentication.  Authentication of a message through a hash(generally only
between the sender and receiver) can be provided with the addition of a shared
secret key between the sender and receiver (possibly exchanged via
Diffie-Hellman) to the message (PGP accomplishes hash authentication through a
public key, usually allowing anyone to authenticate it).  The message (without
the key) and its sender computed hash (using the key) are sent across a wire
where the receiver compares the received hash with the receiver computed hash
using the shared hash function against the received message and the shared key.
This method still allows for deniability among keyholders.  With
authentication, use of a nonce in the hash should also be considered to avoid
a replay attack.  Obviously, anyone only using the hash function against the
message to produce this hash will find no match.  He/she may then assume its a
MAC (message authentication code). If there's no match in the hashes, the
receiver might not know whether the integrity and/or authentication is to
blame.

2: H(message+key)---message,hash--->H(message+key)

A mandatory construction of protocol 2 for internet security protocols is
Bellare's HMAC.

3: H(key XOR opad,H(key XOR ipad,message))


----[  Confidentiality

While a hash MAC provides for message integrity and authentication, there is no
confidentiality to the message using this method.  However, a form of message
confidentiality using hashes can be achieved with the addition of a few simple
steps.  In addition to the message and key, the sender will also add a secret
message to be hashed.  The message (without the key and secret message) and its
sender computed hash (using the key and secret message) are sent across a wire
where the receiver compares the received hash with the receiver computed hash
using the shared hash function against the received message, shared key, and
secret message.  A receiver may first wish to check if the hash is a MAC, then
look for a secret message.  If there's no match in the hashes, he/she might not
know whether the integrity, authentication, and/or failure to determine the
secret is to blame.

4: H(public message+key+secret message)---public message,hash--->H(public
   message+key+secret message)

For HMAC, the secret message can be appended to the public message.

5: H(key XOR opad,H(key XOR ipad,public message+secret message))

The obvious question for the receiver is how to choose the right secret message
to get the hash to compute correctly.  The answer is to use a brute force
method using a dictionary of possible secret messages, a method similar to
those of password cracking programs with the public message used as the salt.
While this may sound unfeasible at first, the choice of a "secret message"
dictionary with a reasonable search space (its granularity via letters, words,
and/or phrases), the orderliness of the dictionary(sorted by most commonly
used to least), a decent hash speed (the size of the secret message is not a
factor), and/or performing the hash computations in parallel can simplify
brute forcing into a workable solution. In addition to figuring out the secret
message, integrity and authentication of both the public and secret messages
will also be guaranteed.


----[  Steganography

By now, it should be obvious from what is sent over the wire in protocols 2 and
4 that hash confidentiality also has a steganographic (hidden) property.
Hashes used as one-time pads or in wheat/chaff operations for confidentiality
don't possess this property.  In a variation on this method, another stego
version of this would be to take out the public message.  Some applications
such as S/key only send hashes over the wire at certain points in their
protocols.

6: H(key+secret message)---hash--->H(key+secret message)

The strength of the encryption method lies in the strength of the underlying
MAC (hash function, key length, key reuse, and construction).  The strength of
the steganographic method lies in giving the impression that only a MAC is
being used: minimizing public message reuse, keeping others ignorant of the
stego hasho construction formula, and using the most conservative number of
stego hashes to convey a large message(this relates to dictionary granularity).
If secret messages need to be tied together in sequential order to form a
larger message, using a nonce such as a timestamp in each message for
sequencing will suffice (or adopting an external sequence number such as is
found in network packets).  The stego property can still be maintained because
MACs use nonces.  Examples where a larger message could be sent without much
suspicion could involve a stream of authenticated IPv6 packets or the transfer
of a list of files and their corresponding checksums.  As far as cryptanalysis,
steganalysis, and other attacks are concerned, strong hash function and
construction is important. Also, frequent changes in the public message and
secret key help.  If a particular hash or construction flaw causes the
encryption to be broken, change to a more secure one. However, old secret
messages may be compromised.

It's kind of ironic that this is a stego method based on embedding a secret
into ciphertext (hash), based on a misguided notion as to the ciphertext's
function.  Other stego methods(such as using image bits) are weaker and may
involve more overhead, though they may be strengthened by encrypting the
embedded secret.

Example of stego hasho with HMAC construction (source available from RFC2104)
using MD5 hash (source available from RFC1321) and on-line English dictionary
(source available from your local cracker).


----[  The Code

<++> P55/Stego-hasho/example.c !55654cc3
/*stego hasho exampleo */
#include <time.h>
#include <stdio.h>
#include <string.h>

int
main ()
{
  char shared_secret_key[8];
  char dictionary_word[20];
  char message[100];
  char public_message[50];
  time_t timestamp_nonce;
  char secret_message[20];
  unsigned char sender_sent_digest[16],receiver_computed_digest[16];
  int i;

  FILE *inFile = fopen ("english.dictionary", "r");
  printf ("HMAC-MD5 Stego Hasho\n");
  printf ("Sender-\n");
  printf ("Input shared secret key:");
  gets(shared_secret_key);
  printf ("Input public message:");
  gets(public_message);
  time (&timestamp_nonce);
  printf ("Input secret message:");
  gets(secret_message);
  printf ("Creating hash\n");
  sprintf(message,"%s%d",public_message,timestamp_nonce);
  strcat(message,secret_message);
  hmac_md5(message, strlen(message), shared_secret_key,
           strlen(shared_secret_key), sender_sent_digest);
  printf ("Sent across wire from sender to receiver-\nmessage:%s%d hash:",
          public_message,timestamp_nonce);
  for (i = 0; i < 16; i++)
    printf ("%02x", sender_sent_digest[i]);
  printf ("\nReceiver-\n");
  printf ("See if only MAC\n");
  sprintf(message,"%s%d",public_message,timestamp_nonce);
  hmac_md5(message, strlen(message), shared_secret_key,
           strlen(shared_secret_key), receiver_computed_digest);
  printf ("MAC hash:");
  for (i = 0; i < 16; i++)
    printf ("%02x",receiver_computed_digest[i]);
  if (bcmp(sender_sent_digest,receiver_computed_digest,16) != 0)
    printf ("\nNot a MAC!\n");
  else {
    printf ("\nIt's a MAC!\n");
    fclose(inFile);
    exit(0);
  }    
  printf ("Finding secret message\n");
  while (fscanf(inFile,"%s",dictionary_word) != EOF) {
    sprintf(message,"%s%d",public_message,timestamp_nonce);
    strcat(message,dictionary_word);
    hmac_md5(message, strlen(message), shared_secret_key,
             strlen(shared_secret_key), receiver_computed_digest);
    if (bcmp(sender_sent_digest,receiver_computed_digest,16) == 0) {
      printf ("Dictionary word hash:");
      for (i = 0; i < 16; i++)
        printf ("%02x", receiver_computed_digest[i]);
      printf ("\nThe secret message is %s!\n",dictionary_word);
      break;
    }
  }
  if (bcmp(sender_sent_digest,receiver_computed_digest,16) != 0)
    printf ("The secret message was not found!\n");
  fclose(inFile);
}
<-->

Sample Run:
HMAC-MD5 Stego Hasho
Sender-
Input shared secret key:test
Input public message:this is a test
Input secret message:omega
Creating hash
Sent across wire from sender to receiver-
message:this is a test915085524 hash:9b7ba39ec743b0eaaccbc08aaa51565b
Receiver-
See if only MAC
MAC hash:324d28bc83e881782914b32812c97152
Not a MAC!
Finding secret message
Dictionary word hash:9b7ba39ec743b0eaaccbc08aaa51565b
The secret message is omega!


Source Code (successfully compiled in SunOS environment)
-------------------------------------------------------
Makefile
--------
<++> P55/Stego-hasho/Makefile !681efd3d
CC   =  cc

md5driver: md5driver.o hmac.o md5.o
	$(CC) -o md5driver md5driver.o hmac.o md5.o

example: hmac.o example.o md5driver.o md5.o
	$(CC) -o example hmac.o md5driver.o md5.o
<-->

md5.h
-----
<++> P55/Stego-hasho/md5.h !e95d4a1b
#include <memory.h>

/*
 ***********************************************************************
 ** md5.h -- header file for implementation of MD5                    **
 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
 ** Created: 2/17/90 RLR                                              **
 ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version               **
 ** Revised (for MD5): RLR 4/27/91                                    **
 **   -- G modified to have y&~z instead of y&z                       **
 **   -- FF, GG, HH modified to add in last register done             **
 **   -- Access pattern: round 2 works mod 5, round 3 works mod 3     **
 **   -- distinct additive constant for each step                     **
 **   -- round 4 added, working mod 7                                 **
 ***********************************************************************
 */

/*
 ***********************************************************************
 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
 **                                                                   **
 ** License to copy and use this software is granted provided that    **
 ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
 ** Digest Algorithm" in all material mentioning or referencing this  **
 ** software or this function.                                        **
 **                                                                   **
 ** License is also granted to make and use derivative works          **
 ** provided that such works are identified as "derived from the RSA  **
 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
 ** material mentioning or referencing the derived work.              **
 **                                                                   **
 ** RSA Data Security, Inc. makes no representations concerning       **
 ** either the merchantability of this software or the suitability    **
 ** of this software for any particular purpose.  It is provided "as  **
 ** is" without express or implied warranty of any kind.              **
 **                                                                   **
 ** These notices must be retained in any copies of any part of this  **
 ** documentation and/or software.                                    **
 ***********************************************************************
 */

/*#define bcopy(x,y,n) memmove(y,x,n)
#define bzero(x,y) memset(x,0,y)
#define bcmp(x,y,n) memcmp(x,y,n)*/

/* typedef a 32-bit type */
typedef unsigned long int UINT4;

/* Data structure for MD5 (Message-Digest) computation */
typedef struct {
  UINT4 i[2];                   /* number of _bits_ handled mod 2^64 */
  UINT4 buf[4];                                    /* scratch buffer */
  unsigned char in[64];                              /* input buffer */
  unsigned char digest[16];     /* actual digest after MD5Final call */
} MD5_CTX;

void MD5Init ();
void MD5Update ();
void MD5Final ();

/*
 ***********************************************************************
 ** End of md5.h                                                      **
 ******************************** (cut) ********************************
 */
<-->
md5.c
-----
<++> P55/Stego-hasho/md5.c !bd76c633
/*
 ***********************************************************************
 ** md5.c -- the source code for MD5 routines                         **
 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
 ** Created: 2/17/90 RLR                                              **
 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
 ** Revised: 6/99 Conehead                                            **
 ***********************************************************************
 */

/*
 ***********************************************************************
 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
 **                                                                   **
 ** License to copy and use this software is granted provided that    **
 ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
 ** Digest Algorithm" in all material mentioning or referencing this  **
 ** software or this function.                                        **
 **                                                                   **
 ** License is also granted to make and use derivative works          **
 ** provided that such works are identified as "derived from the RSA  **
 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
 ** material mentioning or referencing the derived work.              **
 **                                                                   **
 ** RSA Data Security, Inc. makes no representations concerning       **
 ** either the merchantability of this software or the suitability    **
 ** of this software for any particular purpose.  It is provided "as  **
 ** is" without express or implied warranty of any kind.              **
 **                                                                   **
 ** These notices must be retained in any copies of any part of this  **
 ** documentation and/or software.                                    **
 ***********************************************************************
 */

#include "md5.h"

/*
 ***********************************************************************
 **  Message-digest routines:                                         **
 **  To form the message digest for a message M                       **
 **    (1) Initialize a context buffer mdContext using MD5Init        **
 **    (2) Call MD5Update on mdContext and M                          **
 **    (3) Call MD5Final on mdContext                                 **
 **  The message digest is now in mdContext->digest[0...15]           **
 ***********************************************************************
 */

/* forward declaration */
static void Transform ();

static unsigned char PADDING[64] = {
  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

/* F, G, H and I are basic MD5 functions */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))

/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))

/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) \
  {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
   (a) = ROTATE_LEFT ((a), (s)); \
   (a) += (b); \
  }
#define GG(a, b, c, d, x, s, ac) \
  {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
   (a) = ROTATE_LEFT ((a), (s)); \
   (a) += (b); \
  }
#define HH(a, b, c, d, x, s, ac) \
  {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
   (a) = ROTATE_LEFT ((a), (s)); \
   (a) += (b); \
  }
#define II(a, b, c, d, x, s, ac) \
  {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
   (a) = ROTATE_LEFT ((a), (s)); \
   (a) += (b); \
  }

/* The routine MD5Init initializes the message-digest context
   mdContext. All fields are set to zero.
 */
void MD5Init (mdContext)
MD5_CTX *mdContext;
{
  mdContext->i[0] = mdContext->i[1] = (UINT4)0;

  /* Load magic initialization constants.
   */
  mdContext->buf[0] = (UINT4)0x67452301;
  mdContext->buf[1] = (UINT4)0xefcdab89;
  mdContext->buf[2] = (UINT4)0x98badcfe;
  mdContext->buf[3] = (UINT4)0x10325476;
}

/* The routine MD5Update updates the message-digest context to
   account for the presence of each of the characters inBuf[0..inLen-1]
   in the message whose digest is being computed.
 */
void MD5Update (mdContext, inBuf, inLen)
MD5_CTX *mdContext;
unsigned char *inBuf;
unsigned int inLen;
{
  UINT4 in[16];
  int mdi;
  unsigned int i, ii;

  /* compute number of bytes mod 64 */
  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);

  /* update number of bits */
  if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
    mdContext->i[1]++;
  mdContext->i[0] += ((UINT4)inLen << 3);
  mdContext->i[1] += ((UINT4)inLen >> 29);

  while (inLen--) {
    /* add new character to buffer, increment mdi */
    mdContext->in[mdi++] = *inBuf++;

    /* transform if necessary */
    if (mdi == 0x40) {
      for (i = 0, ii = 0; i < 16; i++, ii += 4)
        in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
                (((UINT4)mdContext->in[ii+2]) << 16) |
                (((UINT4)mdContext->in[ii+1]) << 8) |
                ((UINT4)mdContext->in[ii]);
      Transform (mdContext->buf, in);
      mdi = 0;
    }
  }
}

/* The routine MD5Final terminates the message-digest computation and
   ends with the desired message digest in mdContext->digest[0...15].
 */
void MD5Final (digest,mdContext)
unsigned char *digest;
MD5_CTX *mdContext;
{
  UINT4 in[16];
  int mdi;
  unsigned int i, ii;
  unsigned int padLen;

  /* save number of bits */
  in[14] = mdContext->i[0];
  in[15] = mdContext->i[1];

  /* compute number of bytes mod 64 */
  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);

  /* pad out to 56 mod 64 */
  padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
  MD5Update (mdContext, PADDING, padLen);

  /* append length in bits and transform */
  for (i = 0, ii = 0; i < 14; i++, ii += 4)
    in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
            (((UINT4)mdContext->in[ii+2]) << 16) |
            (((UINT4)mdContext->in[ii+1]) << 8) |
            ((UINT4)mdContext->in[ii]);
  Transform (mdContext->buf, in);

  /* store buffer in digest */
  for (i = 0, ii = 0; i < 4; i++, ii += 4) {
    mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
    mdContext->digest[ii+1] =
      (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
    mdContext->digest[ii+2] =
      (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
    mdContext->digest[ii+3] =
      (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
  }
  bcopy(mdContext->digest,digest,16);
}

/* Basic MD5 step. Transforms buf based on in.
 */
static void Transform (buf, in)
UINT4 *buf;
UINT4 *in;
{
  UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];

  /* Round 1 */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
  FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */
  FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */
  FF ( c, d, a, b, in[ 2], S13,  606105819); /* 3 */
  FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */
  FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */
  FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */
  FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */
  FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */
  FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */
  FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */
  FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */
  FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */
  FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */
  FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */
  FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */
  FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */

  /* Round 2 */
#define S21 5
#define S22 9
#define S23 14
#define S24 20
  GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */
  GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */
  GG ( c, d, a, b, in[11], S23,  643717713); /* 19 */
  GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */
  GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */
  GG ( d, a, b, c, in[10], S22,   38016083); /* 22 */
  GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */
  GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */
  GG ( a, b, c, d, in[ 9], S21,  568446438); /* 25 */
  GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */
  GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */
  GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */
  GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */
  GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */
  GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */
  GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */

  /* Round 3 */
#define S31 4
#define S32 11
#define S33 16
#define S34 23
  HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */
  HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */
  HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */
  HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */
  HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */
  HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */
  HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */
  HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */
  HH ( a, b, c, d, in[13], S31,  681279174); /* 41 */
  HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */
  HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */
  HH ( b, c, d, a, in[ 6], S34,   76029189); /* 44 */
  HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */
  HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */
  HH ( c, d, a, b, in[15], S33,  530742520); /* 47 */
  HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */

  /* Round 4 */
#define S41 6
#define S42 10
#define S43 15
#define S44 21
  II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */
  II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */
  II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */
  II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */
  II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */
  II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */
  II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */
  II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */
  II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */
  II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */
  II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */
  II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */
  II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */
  II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */
  II ( c, d, a, b, in[ 2], S43,  718787259); /* 63 */
  II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */

  buf[0] += a;
  buf[1] += b;
  buf[2] += c;
  buf[3] += d;
}

/*
 ***********************************************************************
 ** End of md5.c                                                      **
 ******************************** (cut) ********************************
 */
<-->
hmac.c
------
<++> P55/Stego-hasho/hmac.c !d4cbaed9
/* sample code from RFC2104 */
#include <string.h>
#include "md5.h"

/*
** Function: hmac_md5
*/

void
hmac_md5(text, text_len, key, key_len, digest)
unsigned char*  text;                /* pointer to data stream */
int             text_len;            /* length of data stream */
unsigned char*  key;                 /* pointer to authentication key */
int             key_len;             /* length of authentication key */
unsigned char * digest;              /* caller digest to be filled in */

{
        MD5_CTX context;
        unsigned char k_ipad[65];    /* inner padding -
                                      * key XORd with ipad
                                      */
        unsigned char k_opad[65];    /* outer padding -
                                      * key XORd with opad
                                      */
        unsigned char tk[16];
        int i;
        /* if key is longer than 64 bytes reset it to key=MD5(key) */
        if (key_len > 64) {

                MD5_CTX      tctx;

                MD5Init(&tctx);
                MD5Update(&tctx, key, key_len);
                MD5Final(tk, &tctx);

                key = tk;
                key_len = 16;
        }

        /*
         * the HMAC_MD5 transform looks like:
         *
         * MD5(K XOR opad, MD5(K XOR ipad, text))
         *
         * where K is an n byte key
         * ipad is the byte 0x36 repeated 64 times
         * opad is the byte 0x5c repeated 64 times
         * and text is the data being protected
         */

        /* start out by storing key in pads */
        bzero( k_ipad, sizeof k_ipad);
        bzero( k_opad, sizeof k_opad);
        bcopy( key, k_ipad, key_len);
        bcopy( key, k_opad, key_len);

        /* XOR key with ipad and opad values */
        for (i=0; i<64; i++) {
                k_ipad[i] ^= 0x36;
                k_opad[i] ^= 0x5c;
        }
        /*
         * perform inner MD5
         */
        MD5Init(&context);                   /* init context for 1st
                                              * pass */
        MD5Update(&context, k_ipad, 64);     /* start with inner pad */
        MD5Update(&context, text, text_len); /* then text of datagram */
        MD5Final(digest, &context);          /* finish up 1st pass */
        /*
         * perform outer MD5
         */
        MD5Init(&context);                   /* init context for 2nd
                                              * pass */
        MD5Update(&context, k_opad, 64);     /* start with outer pad */
        MD5Update(&context, digest, 16);     /* then results of 1st
                                              * hash */
        MD5Final(digest, &context);          /* finish up 2nd pass */
}
<-->
md5driver.c
-----------
<++> P55/Stego-hasho/md5driver.c !508d7874
/*
 ***********************************************************************
 ** md5driver.c -- sample test routines                               **
 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
 ** Created: 2/16/90 RLR                                              **
 ** Updated: 1/91 SRD                                                 **
 ** Updated: 6/99 Conehead                                            **
 ***********************************************************************
 */

/*
 ***********************************************************************
 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
 **                                                                   **
 ** RSA Data Security, Inc. makes no representations concerning       **
 ** either the merchantability of this software or the suitability    **
 ** of this software for any particular purpose.  It is provided "as  **
 ** is" without express or implied warranty of any kind.              **
 **                                                                   **
 ** These notices must be retained in any copies of any part of this  **
 ** documentation and/or software.                                    **
 ***********************************************************************
 */

#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <string.h>
#include "md5.h"

/* Prints message digest buffer in mdContext as 32 hexadecimal digits.
   Order is from low-order byte to high-order byte of digest.
   Each byte is printed with high-order hexadecimal digit first.
 */
static void MDPrint (mdContext)
MD5_CTX *mdContext;
{
  int i;

  for (i = 0; i < 16; i++)
    printf ("%02x", mdContext->digest[i]);
}

/* size of test block */
#define TEST_BLOCK_SIZE 1000

/* number of blocks to process */
#define TEST_BLOCKS 10000

/* number of test bytes = TEST_BLOCK_SIZE * TEST_BLOCKS */
static long TEST_BYTES = (long)TEST_BLOCK_SIZE * (long)TEST_BLOCKS;

/* A time trial routine, to measure the speed of MD5.
   Measures wall time required to digest TEST_BLOCKS * TEST_BLOCK_SIZE
   characters.
 */
static void MDTimeTrial ()
{
  MD5_CTX mdContext;
  time_t endTime, startTime;
  unsigned char data[TEST_BLOCK_SIZE];
  unsigned int i;
  unsigned char digest[16];

  /* initialize test data */
  for (i = 0; i < TEST_BLOCK_SIZE; i++)
    data[i] = (unsigned char)(i & 0xFF);

  /* start timer */
  printf ("MD5 time trial. Processing %ld characters...\n", TEST_BYTES);
  time (&startTime);

  /* digest data in TEST_BLOCK_SIZE byte blocks */
  MD5Init (&mdContext);
  for (i = TEST_BLOCKS; i > 0; i--)
    MD5Update (&mdContext, data, TEST_BLOCK_SIZE);
  MD5Final (digest,&mdContext);

  /* stop timer, get time difference */
  time (&endTime);
  MDPrint (&mdContext);
  printf (" is digest of test input.\n");
  printf
    ("Seconds to process test input: %ld\n", (long)(endTime-startTime));
  printf
    ("Characters processed per second: %ld\n",
     TEST_BYTES/(endTime-startTime));
}

/* Computes the message digest for string inString.
   Prints out message digest, a space, the string (in quotes) and a
   carriage return.
 */
static void MDString (inString)
char *inString;
{
  MD5_CTX mdContext;
  unsigned int len = strlen (inString);
  unsigned char digest[16];

  MD5Init (&mdContext);
  MD5Update (&mdContext, inString, len);
  MD5Final (digest,&mdContext);
/*  MDPrint (&mdContext);
  printf (" \"%s\"\n", inString);*/
}

/* Computes the message digest for a specified file.
   Prints out message digest, a space, the file name, and a carriage
   return.
 */
static void MDFile (filename)
char *filename;
{
  FILE *inFile = fopen (filename, "rb");
  MD5_CTX mdContext;
  int bytes;
  unsigned char data[1024];
  unsigned char digest[16];

  if (inFile == NULL) {
    printf ("%s can't be opened.\n", filename);
    return;
  }

  MD5Init (&mdContext);
  while ((bytes = fread (data, 1, 1024, inFile)) != 0)
    MD5Update (&mdContext, data, bytes);
  MD5Final (digest,&mdContext);
  MDPrint (&mdContext);
  printf (" %s\n", filename);
  fclose (inFile);
}

/* Writes the message digest of the data from stdin onto stdout,
   followed by a carriage return.
 */
static void MDFilter ()
{
  MD5_CTX mdContext;
  int bytes;
  unsigned char data[16];
  unsigned char digest[16];

  MD5Init (&mdContext);
  while ((bytes = fread (data, 1, 16, stdin)) != 0)
    MD5Update (&mdContext, data, bytes);
  MD5Final (digest,&mdContext);
  MDPrint (&mdContext);
  printf ("\n");
}

/* Runs a standard suite of test data.
 */
static void MDTestSuite ()
{
  printf ("MD5 test suite results:\n");
  MDString ("");
  MDString ("a");
  MDString ("abc");
  MDString ("message digest");
  MDString ("abcdefghijklmnopqrstuvwxyz");
  MDString
    ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
  MDString
    ("1234567890123456789012345678901234567890\
1234567890123456789012345678901234567890");
  /* Contents of file foo are "abc" */
  MDFile ("foo");
}

static void MDTestDictionary ()
{
  char word[100];
  unsigned char digest[16];

  FILE *inFile = fopen ("/usr/dict/words", "r");
  printf ("MD5 dictionary results:\n");
  while (fscanf(inFile,"%s",word) != EOF)
    hmac_md5(word, strlen(word), "testkey", strlen("testkey"), digest);
  fclose(inFile);
}

static void MDTestStegoHasho ()
{
  char key[100];
  char word[100];
  char message[100];
  char public[50];
  time_t timestamp;
  char secret[50];
  unsigned char digest1[16],digest2[16];
  int i;

  FILE *inFile = fopen ("/usr/dict/words", "r");
  printf ("MD5 Stego Hasho\n");
  printf ("Sender-\n");
  printf ("Input shared secret key:");
  gets(key);
  printf ("Input public message:");
  gets(public);
  time (&timestamp);
  printf ("Input secret message:");
  gets(secret);
  printf ("Creating hash\n");
  sprintf(message,"%s%d",public,timestamp);
  strcat(message,secret);
  hmac_md5(message, strlen(message), key, strlen(key), digest1);
  printf ("Sent across wire from sender to receiver-\nmessage:%s%d hash:",
          public,timestamp);
  for (i = 0; i < 16; i++)
    printf ("%02x", digest1[i]);
  printf ("\nReceiver-\n");
  printf ("See if only MAC\n");
  sprintf(message,"%s%d",public,timestamp);
  hmac_md5(message, strlen(message), key, strlen(key), digest2);
  printf ("MAC hash:");
  for (i = 0; i < 16; i++)
    printf ("%02x", digest2[i]);
  if (bcmp(digest1,digest2,16) != 0)
    printf ("\nNot a MAC!\n");
  else {
    printf ("\nIt's a MAC!\n");
    fclose(inFile);
    exit(0);
  }    
  printf ("Finding secret message\n");
  while (fscanf(inFile,"%s",word) != EOF) {
    sprintf(message,"%s%d",public,timestamp);
    strcat(message,word);
    hmac_md5(message, strlen(message), key, strlen(key), digest2);
    if (bcmp(digest1,digest2,16) == 0) {
      printf ("Dictionary word hash:");
      for (i = 0; i < 16; i++)
        printf ("%02x", digest2[i]);
      printf ("\nThe secret message is %s!\n",word);
      break;
    }
  }
  if (bcmp(digest1,digest2,16) != 0)
    printf ("The secret message was not found!\n");
  fclose(inFile);
}

int main (argc, argv)
int argc;
char *argv[];
{
  int i;

  /* For each command line argument in turn:
  ** filename          -- prints message digest and name of file
  ** -d                -- prints time trial of whole dictionary
  ** -h                -- performs stego hasho
  ** -sstring          -- prints message digest and contents of string
  ** -t                -- prints time trial statistics for 10M
                          characters
  ** -x                -- execute a standard suite of test data
  ** (no args)         -- writes messages digest of stdin onto stdout
  */
  if (argc == 1)
    MDFilter ();
  else
    for (i = 1; i < argc; i++)
      if (argv[i][0] == '-' && argv[i][1] == 's')
        MDString (argv[i] + 2);
      else if (strcmp (argv[i], "-d") == 0)
        MDTestDictionary ();
      else if (strcmp (argv[i], "-h") == 0)
        MDTestStegoHasho ();
      else if (strcmp (argv[i], "-t") == 0)
        MDTimeTrial ();
      else if (strcmp (argv[i], "-x") == 0)
        MDTestSuite ();
      else MDFile (argv[i]);

  return(0);
}

/*
 ***********************************************************************
 ** End of md5driver.c                                                **
 ******************************** (cut) ********************************
 */
<-->
----[  EOF
