-------[ 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#include #include 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 (×tamp;_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 /* *********************************************************************** ** 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 #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 #include #include #include #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 (×tamp;); 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