Valhalla Legends Forums Archive | Battle.net Bot Development | [C]MD5

AuthorMessageTime
HdxBmx27
Well I decided I wanted to do MD5 verification of Warden modules, but then relized that I 1) Had no implementation of it in C, and 2) All the code I found online was really really fucking ugly. (Exa: here MD5STEP WTFUX?)

So I did a bit of research and realized that MD5 is damn near SHA1 [Makes sense as it's its predecessor].
So a bit of cleaning up I ended up with this:
[code]#ifndef MD5_H
#define MD5_H

#include "stdint.h"
#include "math.h"

#ifndef _MD5_enum_
#define _MD5_enum_
enum{
    md5_success = 0,
    md5_null,            /* Null pointer parameter */
    md5_input_too_long,  /* input data too long */
    md5_state_error      /* called Input after Result */
};
#endif
#define md5_hash_size 16

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

typedef struct md5_context{
  uint32_t      intermediate_hash[md5_hash_size / 4]; /* Message Digest                  */
  uint32_t      length_low;                          /* Message length in bits          */
  uint32_t      length_high;                          /* Message length in bits          */
  int_least16_t message_block_index;                  /* Index into message block array  */
  uint8_t      message_block[64];                    /* 512-bit message blocks          */
  uint8_t      computed;                            /* Is the digest computed?          */
  uint8_t      corrupted;                            /* Is the message digest corrupted? */
} md5_context;

int __stdcall md5_reset(md5_context *);
int __stdcall md5_input(md5_context *, const uint8_t *, uint32_t);
int __stdcall md5_digest(md5_context *, uint8_t *);
int __stdcall md5_verify_data(uint8_t *, uint32_t, const uint8_t *);

#endif[/code][code]#include "md5.h"

void md5_process_message_block(md5_context *);

#define md5_batoi(ba, i) \
  ((ba[i+3] << 24) | (ba[i+2] << 16) | (ba[i+1] << 8) | ba[i])

#define md5_rol(word, bits) \
    (((word) << (bits)) | ((word) >> (32-(bits))))


#define md5_itoba(a, ba, i) \
  (ba[i+3] = (uint8_t)(a >> 24)); (ba[i+2] = (uint8_t)(a >> 16)); (ba[i+1] = (uint8_t)(a >> 8)); (ba[i] = (uint8_t)a);

uint32_t md5_math(uint16_t t, uint32_t B, uint32_t C, uint32_t D){
  if(t < 16)      return (D ^ (B & (C ^ D)));
  else if(t < 32) return (C ^ (D & (B ^ C)));
  else if(t < 48) return (B ^ C ^ D);
  else            return (C ^ (B | ~D));
}
uint16_t md5_index(uint16_t t){
  if(t < 16)      return t;
  else if(t < 32) return (5 * t + 1) % 16;
  else if(t < 48) return (3 * t + 5) % 16;
  else            return (7 * t)    % 16;
}
uint16_t md5_shift(uint16_t t){
  if(t < 16)      return (((t % 4) + 1) * 5 + 2);
  else if(t < 32) return (t % 4 == 0 ? 5 : (t % 4 == 1 ?  9 : (t % 4 == 2 ? 14 : 20)));
  else if(t < 48) return (t % 4 == 0 ? 4 : (t % 4 == 1 ? 11 : (t % 4 == 2 ? 16 : 23)));
  else            return (t % 4 == 0 ? 6 : (t % 4 == 1 ? 10 : (t % 4 == 2 ? 15 : 21)));
}

int __stdcall md5_reset(md5_context *ctx){
  uint8_t x = 0;
 
  if(!ctx)
    return md5_null;

  ctx->length_low  = 0;
  ctx->length_high = 0;
  ctx->computed    = 0;
  ctx->corrupted  = 0;
  ctx->message_block_index = 0;

  for(x = 0; x < 64; x++)
    ctx->message_block[x] = 0;
 
  ctx->intermediate_hash[0] = 0x67452301;
  ctx->intermediate_hash[1] = 0xEFCDAB89;
  ctx->intermediate_hash[2] = 0x98BADCFE;
  ctx->intermediate_hash[3] = 0x10325476;

  return md5_success;
}
int __stdcall md5_input(md5_context *ctx, const uint8_t *data, uint32_t length){
  uint32_t x;
  if(!length)
    return md5_success;

  if(!ctx || !data)
    return md5_null;

  if(ctx->computed){
    ctx->corrupted = md5_state_error;
    return md5_state_error;
  }

  for(x = 0; x < length; x++){
    ctx->message_block[ctx->message_block_index++] = (data[x] & 0xFF);
    ctx->length_low += 8;

    if (ctx->length_low == 0){
      ctx->length_high++;
      if(ctx->length_high == 0){
        ctx->corrupted = md5_input_too_long;
        return md5_input_too_long;
      }
    }

    if(ctx->message_block_index == 64)
      md5_process_message_block(ctx);
  }
  return md5_success;
}
int __stdcall md5_digest(md5_context *ctx, uint8_t *digest){
  int i;

  if (!ctx || !digest)
    return md5_null;

  if (ctx->corrupted)
    return ctx->corrupted;

  if (!ctx->computed){
    if (ctx->message_block_index > 55){
      ctx->message_block[ctx->message_block_index++] = 0x80;
 
      while(ctx->message_block_index < 64)
        ctx->message_block[ctx->message_block_index++] = 0;
   
      md5_process_message_block(ctx);
    }else{
      ctx->message_block[ctx->message_block_index++] = 0x80;
    }
 
    while(ctx->message_block_index < 56)
      ctx->message_block[ctx->message_block_index++] = 0;

    md5_itoba(ctx->length_high, ctx->message_block, 60);
    md5_itoba(ctx->length_low,  ctx->message_block, 56);
   
    md5_process_message_block(ctx);
   
    ctx->length_low  = 0;
    ctx->length_high = 0;
    ctx->computed    = 1;
  }

  for(i = 0; i < 4; i++){
    md5_itoba(ctx->intermediate_hash[i], digest, i * 4);
  }

  return md5_success;
}

void md5_process_message_block(md5_context *ctx){
  uint16_t t;          /* Loop counter        */
  uint32_t temp;      /* Temporary word value*/
  uint32_t W[16];      /* Word sequence      */
  uint32_t A, B, C, D; /* Word buffers        */
  const uint32_t K[] = { /* K = floor(abs(sin(x+1) & (2 pow 32))) */
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
  };

  for(t = 0; t < 16; t++)
    W[t] = md5_batoi(ctx->message_block, t * 4);
   
  A = ctx->intermediate_hash[0];
  B = ctx->intermediate_hash[1];
  C = ctx->intermediate_hash[2];
  D = ctx->intermediate_hash[3];

  for(t = 0; t < 64; t++){
    temp = B + md5_rol((A + md5_math(t, B, C, D) + W[md5_index(t)] + K[t]), md5_shift(t));
    A = D;
    D = C;
    C = B;
    B = temp;
  }

  ctx->intermediate_hash[0] += A;
  ctx->intermediate_hash[1] += B;
  ctx->intermediate_hash[2] += C;
  ctx->intermediate_hash[3] += D;

  ctx->message_block_index = 0;
}

int __stdcall md5_verify_data(uint8_t *data, uint32_t length, const uint8_t *correct_md5){
md5_context ctx;
uint8_t digest[16];
uint32_t x;
  md5_reset(&ctx);
md5_input(&ctx, data, length);
md5_digest(&ctx, digest);

  if(!correct_md5)
return 0;

for(x = 0; x < 16; x++){
if(digest[x] != correct_md5[x])
return 0;
}

return 1;
}[/code]

Note, its not perfect [it works 100% but meh] Its not efficient, its not as clean as it could be, but hey, It looks better then the MD5STEP crap.

If anyone has suggestions on ways to make it cleaner/better ive it a shout.
I really want to replace that block of ints, but C dosen't like doing 64-bit math.
To gen K it looks like this:
[code]for(int x = 0; x < 64; x++)
  K[x] = floor(asb(((uint64_t)sin(x + 1)) << 32))[/code]
April 18, 2009, 6:31 AM
Myndfyr
What was wrong with this, which is the first result from Googling "C MD5"?

Mostly I'm just curious; it looked great to me.  I don't see your test cases.
April 18, 2009, 7:53 AM
HdxBmx27
[code]#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 */[/code]
That, it works fine, its just... eww.
I also jsut noticed I put this in the wrong forum, oh well.
April 18, 2009, 7:55 AM
Yegg
I also have a version that looks pretty similar to the one MyndFyre just provided, though not identical. This one also came from searching Google and it looks pretty nice.
April 18, 2009, 12:06 PM
BreW
I don't think that hdx realizes those "ugly" versions of MD5 have their loops unrolled for performance reasons.
April 18, 2009, 1:36 PM
HdxBmx27
[quote author=brew link=topic=17911.msg182424#msg182424 date=1240061809]
I don't think that hdx realizes those "ugly" versions of MD5 have their loops unrolled for performance reasons.
[/quote]I Do
April 18, 2009, 5:37 PM
Quarantine
Yeah, I don't really know why they'd use a Macro vs an Inline Function.
April 19, 2009, 6:29 PM
Myndfyr
[quote author=Warrior link=topic=17911.msg182449#msg182449 date=1240165781]
Yeah, I don't really know why they'd use a Macro vs an Inline Function.
[/quote]
Because an inline function isn't always guaranteed to be inlined.
April 20, 2009, 4:31 AM

Search