Yummy Digests

I've been working on a few projects that require blitzmax and a web-server (php/mysql) to communicate. Along came the need to hash files. This code archive entry was very useful :) . I decided to wrap it into  an object and add a new method to it. Even though it's a series of functions I still tend to make types with functions in them to help me organize them (it's like a cheap way of  namespacing). I needed the ability to hash a file using Sha-1 hashing. Seeing that php has a function called sha1_file($pathToFile) I figured I'd implement the equivalent on blitzmax. With this THasher type, I've added a SHA1_File(path) function also.

Why hash files?

There are many reasons to use hashes, but recently I used it to check for file changes. If you've ever wanted to find out if some arbitrary file has 'changed' since the last time you've opened it this can prove to be useful. If you save the hash beforehand then recompute the hash now and if they are different, then something's changed! Some applications of this are:

  • Write a file-updater to quickly find out which new files need to be downloaded. Just comparing hashes would be sufficient in most cases, instead of having to go individually byte-by-byte or traversing the file structure to find a difference.
  • help stop cheating. If you don't want users changing texture files to gain an advantage or don't want them 'mucking' with any other data file. If you check the current hash against a previously approved one and it doesn't check out then the file has more than likely been tampered with.

How Do I Use THasher?

If you want to hash a string using SHA-1 then:

Local hashedFoo:String = THasher.sha1("Foo")

Want to hash a whole file? Then:

Local filehash:String = THasher.SHA1_File("someFile.txt")

Keep in mind you can hash any file, not just text files like in the above example.

Anyway, read on for the whole file. With the exception of SHA1_File() I did not write these hashing functions for Blitzmax. They were simply taken from Yan's very helpful code archives post :) .

Strict

Type THasher

    Function MD5:String(in:String)
        Local h0:Int = $67452301, h1 = $EFCDAB89, h2 = $98BADCFE, h3 = $10325476
      
        Local r:Int[] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,  ..
                   5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,  ..
                   4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,  ..
                   6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]
                  
        Local k:Int[] = [$D76AA478, $E8C7B756, $242070DB, $C1BDCEEE, $F57C0FAF, $4787C62A,  ..
                   $A8304613, $FD469501, $698098D8, $8B44F7AF, $FFFF5BB1, $895CD7BE,  ..
                   $6B901122, $FD987193, $A679438E, $49B40821, $F61E2562, $C040B340,  ..
                   $265E5A51, $E9B6C7AA, $D62F105D, $02441453, $D8A1E681, $E7D3FBC8,  ..
                   $21E1CDE6, $C33707D6, $F4D50D87, $455A14ED, $A9E3E905, $FCEFA3F8,  ..
                   $676F02D9, $8D2A4C8A, $FFFA3942, $8771F681, $6D9D6122, $FDE5380C,  ..
                   $A4BEEA44, $4BDECFA9, $F6BB4B60, $BEBFBC70, $289B7EC6, $EAA127FA,  ..
                   $D4EF3085, $04881D05, $D9D4D039, $E6DB99E5, $1FA27CF8, $C4AC5665,  ..
                   $F4292244, $432AFF97, $AB9423A7, $FC93A039, $655B59C3, $8F0CCC92,  ..
                   $FFEFF47D, $85845DD1, $6FA87E4F, $FE2CE6E0, $A3014314, $4E0811A1,  ..
                   $F7537E82, $BD3AF235, $2AD7D2BB, $EB86D391]
                  
        Local intCount = (((in:String.length + 8) Shr 6) + 1) Shl 4
        Local data[intCount]
    
        For Local c = 0 Until in:String.length
          data[c Shr 2] = data[c Shr 2] | ((in:String[c] & $FF) Shl ((c & 3) Shl 3))
        Next
        data[in:String.length Shr 2] = data[in:String.length Shr 2] | ($80 Shl ((in:String.length & 3) Shl 3))
        data[data.length - 2] = (Long(in:String.length) * 8) & $FFFFFFFF
        data[data.length - 1] = (Long(in:String.length) * 8) Shr 32
       
        For Local chunkStart = 0 Until intCount Step 16
          Local a = h0, b = h1, c = h2, d = h3
              
          For Local i = 0 To 15
            Local f = d ~ (b & (c ~ d))
            Local t = d
            
            d = c ; c = b
            b = Rol((a + f + k[i] + data[chunkStart + i]), r[i]) + b
            a = t
          Next
          
          For Local i = 16 To 31
            Local f = c ~ (d & (b ~ c))
            Local t = d
       
            d = c ; c = b
            b = Rol((a + f + k[i] + data[chunkStart + (((5 * i) + 1) & 15)]), r[i]) + b
            a = t
          Next
          
          For Local i = 32 To 47
            Local f = b ~ c ~ d
            Local t = d
            
            d = c ; c = b
            b = Rol((a + f + k[i] + data[chunkStart + (((3 * i) + 5) & 15)]), r[i]) + b
            a = t
          Next
          
          For Local i = 48 To 63
            Local f = c ~ (b | ~d)
            Local t = d
            
            d = c ; c = b
            b = Rol((a + f + k[i] + data[chunkStart + ((7 * i) & 15)]), r[i]) + b
            a = t
          Next
          
          h0:+a ; h1:+b
          h2:+c ; h3:+d
        Next
       
        Return (LEHex(h0) + LEHex(h1) + LEHex(h2) + LEHex(h3)).ToLower()
    End Function

    Function SHA1_File:String(path:String)
        If FileType(path) = 1 Then
            Local bytes:Byte[] = New Byte[FileSize(path)]
            Local s:TStream = ReadFile(path)
            If s Then
                s.ReadBytes(bytes, bytes.Length)
                s.Close()
                Local str:String = String.FromBytes(bytes, bytes.length)
                Return THasher.SHA1(str)               
            End If
        End If
        Return ""
    End Function
   
    Function SHA1:String(in:String)
        Local h0 = $67452301, h1 = $EFCDAB89, h2 = $98BADCFE, h3 = $10325476, h4 = $C3D2E1F0
       
        Local intCount = (((in:String.length + 8) Shr 6) + 1) Shl 4
        Local data[intCount]
       
        For Local c = 0 Until in:String.length
          data[c Shr 2] = (data[c Shr 2] Shl 8) | (in:String[c] & $FF)
        Next
        data[in:String.length Shr 2] = ((data[in:String.length Shr 2] Shl 8) | $80) Shl ((3 - (in:String.length & 3)) Shl 3)
        data[data.length - 2] = (Long(in:String.length) * 8) Shr 32
        data[data.length - 1] = (Long(in:String.length) * 8) & $FFFFFFFF
       
        For Local chunkStart = 0 Until intCount Step 16
          Local a = h0, b = h1, c = h2, d = h3, e = h4
       
          Local w[] = data[chunkStart..chunkStart + 16]
          w = w[..80]
          
          For Local i = 16 To 79
            w[i] = Rol(w[i - 3] ~ w[i - 8] ~ w[i - 14] ~ w[i - 16], 1)
          Next
          
          For Local i = 0 To 19
            Local t = Rol(a, 5) + (d ~ (b & (c ~ d))) + e + $5A827999 + w[i]
            
            e = d ; d = c
            c = Rol(b, 30)
            b = a ; a = t
          Next
          
          For Local i = 20 To 39
            Local t = Rol(a, 5) + (b ~ c ~ d) + e + $6ED9EBA1 + w[i]
            
            e = d ; d = c
            c = Rol(b, 30)
            b = a ; a = t
          Next
          
          For Local i = 40 To 59
            Local t = Rol(a, 5) + ((b & c) | (d & (b | c))) + e + $8F1BBCDC + w[i]
            
            e = d ; d = c
            c = Rol(b, 30)
            b = a ; a = t
          Next
       
          For Local i = 60 To 79
            Local t = Rol(a, 5) + (b ~ c ~ d) + e + $CA62C1D6 + w[i]
            
            e = d ; d = c
            c = Rol(b, 30)
            b = a ; a = t
          Next
          
          h0:+a ; h1:+b ; h2:+c
          h3:+d ; h4:+e
        Next
       
        Return (Hex(h0) + Hex(h1) + Hex(h2) + Hex(h3) + Hex(h4)).ToLower()
    End Function

    Function SHA256:String(in:String)
        Local h0 = $6A09E667, h1 = $BB67AE85, h2 = $3C6EF372, h3 = $A54FF53A
        Local h4 = $510E527F, h5 = $9B05688C, h6 = $1F83D9AB, h7 = $5BE0CD19
       
        Local k[] = [$428A2F98, $71374491, $B5C0FBCF, $E9B5DBA5, $3956C25B, $59F111F1,  ..
                      $923F82A4, $AB1C5ED5, $D807AA98, $12835B01, $243185BE, $550C7DC3,  ..
                      $72BE5D74, $80DEB1FE, $9BDC06A7, $C19BF174, $E49B69C1, $EFBE4786,  ..
                      $0FC19DC6, $240CA1CC, $2DE92C6F, $4A7484AA, $5CB0A9DC, $76F988DA,  ..
                      $983E5152, $A831C66D, $B00327C8, $BF597FC7, $C6E00BF3, $D5A79147,  ..
                      $06CA6351, $14292967, $27B70A85, $2E1B2138, $4D2C6DFC, $53380D13,  ..
                      $650A7354, $766A0ABB, $81C2C92E, $92722C85, $A2BFE8A1, $A81A664B,  ..
                      $C24B8B70, $C76C51A3, $D192E819, $D6990624, $F40E3585, $106AA070,  ..
                      $19A4C116, $1E376C08, $2748774C, $34B0BCB5, $391C0CB3, $4ED8AA4A,  ..
                      $5B9CCA4F, $682E6FF3, $748F82EE, $78A5636F, $84C87814, $8CC70208,  ..
                      $90BEFFFA, $A4506CEB, $BEF9A3F7, $C67178F2]
       
        Local intCount = (((in:String.length + 8) Shr 6) + 1) Shl 4
        Local data[intCount]
       
        For Local c = 0 Until in:String.length
          data[c Shr 2] = (data[c Shr 2] Shl 8) | (in:String[c] & $FF)
        Next
        data[in:String.length Shr 2] = ((data[in:String.length Shr 2] Shl 8) | $80) Shl ((3 - (in:String.length & 3)) Shl 3)
        data[data.length - 2] = (Long(in:String.length) * 8) Shr 32
        data[data.length - 1] = (Long(in:String.length) * 8) & $FFFFFFFF
       
        For Local chunkStart = 0 Until intCount Step 16
          Local a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7
       
          Local w[] = data[chunkStart..chunkStart + 16]
          w = w[..64]
          
          For Local i = 16 To 63
            w[i] = w[i - 16] + (Ror(w[i - 15], 7) ~ Ror(w[i - 15], 18) ~ (w[i - 15] Shr 3)) ..
                  + w[i - 7] + (Ror(w[i - 2], 17) ~ Ror(w[i - 2], 19) ~ (w[i - 2] Shr 10))
          Next
          
          For Local i = 0 To 63
            Local t0 = (Ror(a, 2) ~ Ror(a, 13) ~ Ror(a, 22)) + ((a & b) | (b & c) | (c & a))
            Local t1 = h + (Ror(e, 6) ~ Ror(e, 11) ~ Ror(e, 25)) + ((e & f) | (~e & g)) + k[i] + w[i]
            
            h = g ; g = f ; f = e ; e = d + t1
            d = c ; c = b ; b = a ; a = t0 + t1
          Next
          
          h0:+a ; h1:+b ; h2:+c ; h3:+d
          h4:+e ; h5:+f ; h6:+g ; h7:+h
        Next
       
        Return (Hex(h0) + Hex(h1) + Hex(h2) + Hex(h3) + Hex(h4) + Hex(h5) + Hex(h6) + Hex(h7)).ToLower()
    End Function
   
    Function Rol:Int(val:Int, shift:Int)
      Return (val Shl shift) | (val Shr (32 - shift))
    End Function
   
    Function Ror:Int(val:Int, shift:Int)
      Return (val Shr shift) | (val Shl (32 - shift))
    End Function
   
    Function LEHex:String(val:Int)
      Local out:String = Hex(val)
      
      Return out:String[6..8] + out:String[4..6] + out:String[2..4] + out:String[0..2]
    End Function
End Type

Enjoy!

Leave a Comment