MD5 checksum in RPG Maker XP?

Started by orochii, April 06, 2013, 04:51:43 pm

Previous topic - Next topic

orochii

Does anyone had tried generating a MD5 checksum in RPG Maker XP?
I have tried a lot, but can't seem to either get a DLL to work, or make the Digest class to work with RPG Maker (the RGSS ruby doesn't have that class, and it throws me an error when I try to import .so files, which are used by digest.rb).

I've also made an attempt at writing it, but I messed up with something in the algorithm because it doesn't creates a valid MD5 checksum.

Ex.: I use this online generator to verify my resulting checksums.
string: asdfqwer
result: feee448b492937af8fb5b30072d2a78
generator-result: c69874b898abb180ac71bd99bc16f8fb

Here's the code
module MD5
 
  WORD_BUFFER = [0x67452301,0xefcdab89,0x98badcfe,0x10325476]
  SINE_TABLE = [
  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
  ]
  SHIFT_AMOUNT=[ 
  7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21
  ]
 
  def self.calc_md5(string)
    r_a = little_endian(WORD_BUFFER[0]).to_i(16)
    r_b = little_endian(WORD_BUFFER[1]).to_i(16)
    r_c = little_endian(WORD_BUFFER[2]).to_i(16)
    r_d = little_endian(WORD_BUFFER[3]).to_i(16)
    #Create a binary chain from the string
    arr = Array.new
    string.each_byte{|c|
      c.to_i
      arr.push(c)
    }
    n_str = ""
    arr.each {|n|
      a = sprintf("%#b", n)
      a.slice!(0,2)
      n_str += a
    }
   
    #Padding
    n_str += "1"
    old_size = n_str.size
    while n_str.size%512 != 448
      n_str += "0"
    end
    #Prepare size to be appended
    old_size_b = sprintf("%#b", old_size)
    old_size_b.slice!(0,2)
    while old_size_b.size < 64
      old_size_b.insert(0, "0")
    end
    #Append size
    n_str += old_size_b
    #Divide in 512bit chunks
    chunks = []
    (n_str.size/512).times{|i|
      chunks.push n_str.slice( i*512,512 )
    }
    #Iterate each chunk
    chunks.each{|chunk|
      #Divide chunk in 16 32bit words
      words = []
      16.times{|i|
        words.push ((chunk.slice( i*32,32 )).to_i(2))
      }
      #Set temp variables to resulting words
      aa = wrap32(r_a)
      bb = wrap32(r_b)
      cc = wrap32(r_c)
      dd = wrap32(r_d)
      (0..63).each{|i|
        if    (0 <= i) && (i <= 15)
          ff = func(bb,cc,dd)
          g = wrap32(i)
          off_i = 0
        elsif (16<= i) && (i <= 31)
          ff = gunc(bb,cc,dd)
          g = wrap32(((5*i)+1)%16)
          off_i = 4
        elsif (32<= i) && (i <= 47)
          ff = hunc(bb,cc,dd)
          g = wrap32((3*i+5)%16)
          off_i = 8
        elsif (48<= i) && (i <= 63)
          ff = iunc(bb,cc,dd)
          g = wrap32((7*i)%16)
          off_i = 12
        end
       
        dTemp = dd
        dd = cc
        cc = bb
        ii = (i%4)+off_i
        bb = wrap32(bb + leftrotate(aa + ff + little_endian(SINE_TABLE[i]).to_i(16) + words[g], SHIFT_AMOUNT[ii]))
        aa = dTemp
      }
      r_a += aa
      r_b += bb
      r_c += cc
      r_d += dd
      r_a = wrap32(r_a)
      r_b = wrap32(r_b)
      r_c = wrap32(r_c)
      r_d = wrap32(r_d)
     
    }
    rr_a = sprintf("%#x", r_a);rr_b = sprintf("%#x", r_b)
    rr_c = sprintf("%#x", r_c);rr_d = sprintf("%#x", r_d)
    rr_a.slice!(0,2);rr_b.slice!(0,2);rr_c.slice!(0,2);rr_d.slice!(0,2)
    rr_a + rr_b + rr_c + rr_d
  end
 
  def self.leftrotate(x,c)
    b_num = sprintf("%#b", x)
    b_num.slice!(0,2)
    b_num_a = b_num.to_a
    c.times {
      b_num_a.push(b_num_a.shift)
    }
    (b_num_a.to_s).to_i(2)
  end
 
  #// 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)))
 
  def self.func(x,y,z)
    (x & y) | (~(x) & z)
  end
  def self.gunc(x,y,z)
    (x & z) | (y & ~(z))
  end
  def self.hunc(x,y,z)
    (x) ^ (y) ^ (z)
  end
  def self.iunc(x,y,z)
    (y) ^ (x | ~(z))
  end
 
  # Little-endian application to hex-string/number
  def self.little_endian(val,base = :hex)
    val_a = []
    case base
    when :hex
      if !(val.is_a?(String))
        val = sprintf("%#x", val)
        val.slice!(0,2)
      end
      (val.size / 2).times {
        val_a.insert (0,val.slice!(0,2))
      }
    when :bin
      if !(val.is_a?(String))
        val = sprintf("%#b", val)
        val.slice!(0,2)
      end
      (val.size / 16).times {|i|
        val_a.insert (0,val.slice!(0,16))
      }
    end
    val = val_a.to_s
    val
  end
 
  # Wrapping methods (in-module use only, sorry -?-)
  def self.wrap(val,max)
    val%max
  end
  def self.wrap32(val)
    wrap(val,4294967296)
  end
end

I made most stuff by converting to string, mostly because I ignore if there's an actual way to manipulate variable values at bit-level in Ruby.
Also I based "my" algorithm in the pseudocode I found on Wikipedia here.

So, that's it, thanks in advance,
Orochii Zouveleki

Blizzard

On first look, should this here:

string.each_byte{|c|
      c.to_i
      arr.push(c)
    }


be this here:

string.each_byte{|c|
      arr.push(c.to_i)
    }


Also on first sight, are you converting all bytes into strings here? It kinda seems unnecessary since you have bit operators in Ruby as well.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

orochii

Quote from: Blizzard on April 06, 2013, 05:08:57 pm
On first look, should this here:

string.each_byte{|c|
      c.to_i
      arr.push(c)
    }


be this here:

string.each_byte{|c|
      arr.push(c.to_i)
    }

Whoops. But anyway, it gave me same result so that probably was a unnecesary step haha.
QuoteAlso on first sight, are you converting all bytes into strings here? It kinda seems unnecessary since you have bit operators in Ruby as well.

And talking about unnecessary stuff, me passing things to string. I'll recode everything then, just to be sure. I wasn't sure about bit operators, so I did some juggling with strings instead. My mistake heh.

I'll recoding it then, wish me luck (?),
Orochii Zouveleki

orochii

April 12, 2013, 03:05:35 am #3 Last Edit: April 12, 2013, 03:09:53 am by orochii
Sorry for the doublepost, just wanted to say that I finally solved it, but in a different way.
Before starting to code my own MD5 calculating function, I saw an example made in C++. So, I desisted from writing it myself (for now :P) on Ruby, and instead packed it up into a DLL. The result is the following:

module ZOMD5
 @createMD5string = Win32API.new('zomd5.dll', 'GetChecksumFromString', 'pp', 'I')
 def self.calc_md5(string)
   md5 = " "*128
   @createMD5string.call(string,md5)
   md5.unpack("A*")
 end
end
Here's the source and DLL. https://dl.dropboxusercontent.com/u/13006190/zomd5.rar
Original C code is from RSA Data Security, Inc. The C++ "translation" was made by this guy.

Thanks again Blizzard. As I somehow meant, I'll still try it later. For now, I'll try to make a HTTP GET, and if I get that working, I'll finally have my online achievements implemented C:.

Smell ya,
Orochii Zouveleki

Edit: By the way, someone knows of a script (and a demo) where you were able to download files from the web? It's just that I remember seeing one but can't find it (putting "file download script RPG Maker" just gives me dumb results heheheh). Just for reference and stuff.

Blizzard

Well, you could technically rewrite RMX-OS a little bit to be used as a download server instead.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.