| Home Page | Recent Changes

SHA1Hash

Object >> SHA1Hash (custom class)

A custom utility class for calculating Wikipedia logo SHA1 hashes of strings and dynamic byte arrays. It cannot be used in UnrealEngine1 games due to their lack of dynamic arrays.

This implementation is specially optimized for UnrealScript and probably won't perform well when ported to Java, C/C++ or other languages. Important performance optimizations include the use of class-global variables and the lack of out parameters, because those are not "by reference" in UnrealScript!

Note that the bottle neck of this implementation – string to byte conversion – still is there and there's no real workaround for that yet. Passing large strings around takes some time, and they always need to be passed at least to the Mid() function.

Usage

For now the class can only be used statically:

local array<byte> SomeData;
local string Result;

// fill SomeData with byte values

Result = class'SHA1Hash'.static.GetArrayHashString(SomeData);
local string SomeData;
local string Result;

// fill SomeData with a string

Result = class'SHA1Hash'.static.GetStringHashString(SomeData);

A new version is being worked on that allows the programmer to create an instance of the class as "SHA1 context" for more performance-friendly handling of more complex values.

Structs

SHA1Result

A struct for holding the SHA 1 hash value.

int A, B, C, D, E
The five integers making up the 160 bits of the hash.

Methods

Static Methods

SHA1Result GetArrayHash(array<byte> InData)
Returns the SHA1 hash of the specified byte array as a SHA1Result.
string GetArrayHashString(array<byte> InData)
Returns the SHA1 hash of the specified byte array as a string.
SHA1Result GetStringHash(string InData)
Returns the SHA1 hash of the specified string as a SHA1Result.
string GetStringHashString(string InData)
Returns the SHA1 hash of the specified string as a string.
string GetHashString(SHA1Result Hash)
Returns the string representation of the SHA1 hash.
string BigEndianToHex(int i)
Returns the big endian string representation of the integer value.
GetHashBytes(out array<byte> HashBytes, SHA1Result Hash)
Converts the specified SHA1 hash to a byte array and appends that to HashBytes

Code

/******************************************************************************
SHA1 hash implementation for UnrealScript by Wormbo.
Feel free to modify and optimize for your needs.
******************************************************************************/


class SHA1Hash extends Object;


struct SHA1Result { var int A,B,C,D,E; };

/** @ignore */
var private SHA1Result StaticHashValue;
/** @ignore */
var private array<byte> StaticData;


//=============================================================================
// Instant hash functions - probably not suitable for large input data
//=============================================================================

static final function SHA1Result GetStringHash(string In)
{
  local int StrLen, i;
  
  StrLen = Len(In);
  default.StaticData.Length = StrLen;
  for (i = 0; i < StrLen; i++) {
    default.StaticData[i] = Asc(Mid(In, i, 1));
  }
  StaticProcessChunks();
  return default.StaticHashValue;
}

static final function string GetStringHashString(string In)
{
  local int StrLen, i;
  
  StrLen = Len(In);
  default.StaticData.Length = StrLen;
  for (i = 0; i < StrLen; i++) {
    default.StaticData[i] = Asc(Mid(In, i, 1));
  }
  StaticProcessChunks();
  return BigEndianToHex(default.StaticHashValue.A)
      $ BigEndianToHex(default.StaticHashValue.B)
      $ BigEndianToHex(default.StaticHashValue.C)
      $ BigEndianToHex(default.StaticHashValue.D)
      $ BigEndianToHex(default.StaticHashValue.E);
}

static final function SHA1Result GetArrayHash(array<byte> In)
{
  default.StaticData = In;
  StaticProcessChunks();
  return default.StaticHashValue;
}

static final function string GetArrayHashString(array<byte> In)
{
  default.StaticData = In;
  StaticProcessChunks();
  return BigEndianToHex(default.StaticHashValue.A)
      $ BigEndianToHex(default.StaticHashValue.B)
      $ BigEndianToHex(default.StaticHashValue.C)
      $ BigEndianToHex(default.StaticHashValue.D)
      $ BigEndianToHex(default.StaticHashValue.E);
}

static final function string GetHashString(SHA1Result Hash)
{
  return BigEndianToHex(Hash.A) $ BigEndianToHex(Hash.B)
      $ BigEndianToHex(Hash.C) $ BigEndianToHex(Hash.D) $ BigEndianToHex(Hash.E);
}

// Shambler: Limited use, this appends to rather than returning an array because I had special use for it
static final function GetHashBytes(out array<byte> HashBytes, SHA1Result Hash)
{
    local int i;

    i = HashBytes.Length;
    HashBytes.Length = HashBytes.Length + 20;

    HashBytes[i] =      (Hash.A >> 24)  & 0xff;
    HashBytes[i+1] =    (Hash.A >> 16)  & 0xff;
    HashBytes[i+2] =    (Hash.A >> 8)   & 0xff;
    HashBytes[i+3] =    Hash.A      & 0xff;
    HashBytes[i+4] =    (Hash.B >> 24)  & 0xff;
    HashBytes[i+5] =    (Hash.B >> 16)  & 0xff;
    HashBytes[i+6] =    (Hash.B >> 8)   & 0xff;
    HashBytes[i+7] =    Hash.B      & 0xff;
    HashBytes[i+8] =    (Hash.C >> 24)  & 0xff;
    HashBytes[i+9] =    (Hash.C >> 16)  & 0xff;
    HashBytes[i+10] =   (Hash.C >> 8)   & 0xff;
    HashBytes[i+11] =   Hash.C      & 0xff;
    HashBytes[i+12] =   (Hash.D >> 24)  & 0xff;
    HashBytes[i+13] =   (Hash.D >> 16)  & 0xff;
    HashBytes[i+14] =   (Hash.D >> 8)   & 0xff;
    HashBytes[i+15] =   Hash.D      & 0xff;
    HashBytes[i+16] =   (Hash.E >> 24)  & 0xff;
    HashBytes[i+17] =   (Hash.E >> 16)  & 0xff;
    HashBytes[i+18] =   (Hash.E >> 8)   & 0xff;
    HashBytes[i+19] =   Hash.E      & 0xff;
}


//=============================================================================
// Internal stuff for static instant hashing functions
//=============================================================================

static final function string BigEndianToHex(int i)
{
  const hex = "0123456789abcdef";
  
  return Mid(hex, i >> 28 & 0xf, 1) $ Mid(hex, i >> 24 & 0xf, 1)
      $ Mid(hex, i >> 20 & 0xf, 1) $ Mid(hex, i >> 16 & 0xf, 1)
      $ Mid(hex, i >> 12 & 0xf, 1) $ Mid(hex, i >> 8 & 0xf, 1)
      $ Mid(hex, i >> 4 & 0xf, 1) $ Mid(hex, i & 0xf, 1);
}

private static final function StaticProcessChunks()
{
  local int i, chunk, temp;
  local int A, B, C, D, E;
  local array<int> w;
  
  i = default.StaticData.Length;
  if (i % 64 < 56)
    default.StaticData.Length = default.StaticData.Length + 64 - i % 64;
  else
    default.StaticData.Length = default.StaticData.Length + 128 - i % 64;
  default.StaticData[i] = 0x80;
  default.StaticData[default.StaticData.Length - 5] = (i >>> 29);
  default.StaticData[default.StaticData.Length - 4] = (i >>> 21);
  default.StaticData[default.StaticData.Length - 3] = (i >>> 13);
  default.StaticData[default.StaticData.Length - 2] = (i >>>  5);
  default.StaticData[default.StaticData.Length - 1] = (i <<   3);
  
  default.StaticHashValue.A = 0x67452301;
  default.StaticHashValue.B = 0xEFCDAB89;
  default.StaticHashValue.C = 0x98BADCFE;
  default.StaticHashValue.D = 0x10325476;
  default.StaticHashValue.E = 0xC3D2E1F0;
  
  while (chunk * 64 < default.StaticData.Length) {
    w.Length = 80;
    for (i = 0; i < 16; i++) {
      w[i] = (default.StaticData[chunk * 64 + i * 4] << 24)
          | (default.StaticData[chunk * 64 + i * 4 + 1] << 16)
          | (default.StaticData[chunk * 64 + i * 4 + 2] << 8)
          | default.StaticData[chunk * 64 + i * 4 + 3];
    }
    for (i = 16; i < 80; i++) {
      temp = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
      w[i] = (temp << 1) | (temp >>> 31);
    }
    
    // initialize hash value for this chunk
    A = default.StaticHashValue.A;
    B = default.StaticHashValue.B;
    C = default.StaticHashValue.C;
    D = default.StaticHashValue.D;
    E = default.StaticHashValue.E;
    
    // round 1
    E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[ 0] + 0x5A827999;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[ 1] + 0x5A827999;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[ 2] + 0x5A827999;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[ 3] + 0x5A827999;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[ 4] + 0x5A827999;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[ 5] + 0x5A827999;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[ 6] + 0x5A827999;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[ 7] + 0x5A827999;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[ 8] + 0x5A827999;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[ 9] + 0x5A827999;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[10] + 0x5A827999;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[11] + 0x5A827999;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[12] + 0x5A827999;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[13] + 0x5A827999;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[14] + 0x5A827999;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[15] + 0x5A827999;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[16] + 0x5A827999;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[17] + 0x5A827999;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[18] + 0x5A827999;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[19] + 0x5A827999;    C = (C << 30) | (C >>> -30);
    
    // round 2
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1;    C = (C << 30) | (C >>> -30);
    
    // round 3
    E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[40] + 0x8F1BBCDC;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[41] + 0x8F1BBCDC;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[42] + 0x8F1BBCDC;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[43] + 0x8F1BBCDC;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[44] + 0x8F1BBCDC;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[45] + 0x8F1BBCDC;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[46] + 0x8F1BBCDC;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[47] + 0x8F1BBCDC;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[48] + 0x8F1BBCDC;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[49] + 0x8F1BBCDC;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[50] + 0x8F1BBCDC;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[51] + 0x8F1BBCDC;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[52] + 0x8F1BBCDC;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[53] + 0x8F1BBCDC;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[54] + 0x8F1BBCDC;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[55] + 0x8F1BBCDC;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[56] + 0x8F1BBCDC;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[57] + 0x8F1BBCDC;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[58] + 0x8F1BBCDC;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[59] + 0x8F1BBCDC;    C = (C << 30) | (C >>> -30);
    
    // round 4
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6;    C = (C << 30) | (C >>> -30);
    
    E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6;    B = (B << 30) | (B >>> -30);
    D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6;    A = (A << 30) | (A >>> -30);
    C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6;    E = (E << 30) | (E >>> -30);
    B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6;    D = (D << 30) | (D >>> -30);
    A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6;    C = (C << 30) | (C >>> -30);
    
    // add this chunk's hash to result so far
    default.StaticHashValue.A += A;
    default.StaticHashValue.B += B;
    default.StaticHashValue.C += C;
    default.StaticHashValue.D += D;
    default.StaticHashValue.E += E;
    
    chunk++;
  }
}

Implementation Notes

Only the 5 least significant bits of the second operand of UnrealScript's bitshift operators are used. That means, for example (C >>> -30) is the same as (C >>> 2).

The implementation avoids unneccessary circular variable assignments the original SHA1 implementation in RFC 3174 and on Wikipedia suggest. While Wikipedia's very efficient notation of the main loop (see below) is very small, the high number of assignments make it quite slow in UnrealScript.

    for i from 0 to 79
        if 0 ≤ i ≤ 19 then
            f := (b and c) or ((not b) and d)
            k := 0x5A827999
        else if 20 ≤ i ≤ 39
            f := b xor c xor d
            k := 0x6ED9EBA1
        else if 40 ≤ i ≤ 59
            f := (b and c) or (b and d) or (c and d)
            k := 0x8F1BBCDC
        else if 60 ≤ i ≤ 79
            f := b xor c xor d
            k := 0xCA62C1D6
        temp := (a leftrotate 5) + f + e + k + w(i)
        e := d
        d := c
        c := b leftrotate 30
        b := a
        a := temp

Another difference from the original implementation suggestions are the calculations on the first and third round. This algorithm implements the optimizations suggested by Wikipedia: d xor (b and (c xor d)) instead of (b and c) or ((not b) and d) for the first round and (b and c) or (d and (b or c)) instead of (b and c) or (b and d) or (c and d) for the third round. This results in a lower number of operations without changing the result.

Related Topics

  • MD5Hash – a similar class for calculating MD5 hashes
  • Wikipedia logo SHA1 – Wikipedia article about SHA hash functions

Discussion

Jimboh: I was just wondering why you are using 'default' static variables instead of a normal class variable? Is it faster to access default/static data or are you just trying to make it somewhat of a singleton class?

El Muerte: the functions are static thus you can only access default properties. In this case it's safe to use because there are no multiple processes that can access the same data at the same time. If the functions were not static you would need to create an instance first, which isn't the nicest way for utility functions.

Wormbo: I don't think unqualified default variables are accessed faster than unqualified instance variables or local variables. (With "unqualified" I mean "not prefixed with class'ThisClass'. or Class. for the default variable or Self. for the instance variable.)

Switch`: Profiling shows there's a theoretical 10% difference in speed between slowest and fastest access method.

class Sandbox extends Commandlet;

var vector vc, t;

// call 1000000 times
final function profilefunc()
{
    t = ...
}

// results
t = class.default.vc; // 428ms
t = class'Sandbox'.default.vc; // 415ms
t = default.vc; // 385ms
t = vc; // 380ms
t = self.vc; // randomly 406ms - 424ms...

Jimboh: Here is my instanced version of Wormbo's SHA1 implementation. It can be reused as long as Init() is called before each fresh use.

/********************************************************
 * This SHA1 implementation is suitable for large files *
 * and can be read incrementally in aritrary chunks.    *
 * Adapted by Jimboh from Wormbo's                      *
 * SHA1 Hash Implementation for UnrealScript.           *
 ********************************************************/

class SHA1Hash extends Commandlet;

var private int h0, h1, h2, h3, h4;
var private int byteCount;
var private int block;
var private bool digested;

var private int w[80];

final function Init()
{
    h0 = 0x67452301;
    h1 = 0xEFCDAB89;
    h2 = 0x98BADCFE;
    h3 = 0x10325476;
    h4 = 0xC3D2E1F0;
    block = 0;
    byteCount = 0;
    digested = false;
}

final function Update(array<byte> data)
{
    local int byteNum;
    local int len, rem; // Remaining bytes
    local int i, temp;
    local int A, B, C, D, E;

    len = data.Length;
    rem = len;

    while (rem > 0)
    {
        byteNum = byteCount % 4;
        if (rem >= 4 && byteNum == 0)
        {
            w[block] = (data[len - rem] << 24)
                | (data[len - rem + 1] << 16)
                | (data[len - rem + 2] << 8)
                | (data[len - rem + 3]);
            rem -= 4;
            byteCount += 4;
            block++;
        }
        else
        {
            if (byteNum == 0)
                w[block] = 0;
            w[block] = w[block] | (data[len - rem] << ((3 - byteNum) * 8));
            rem--;
            byteCount++;
            if (byteNum == 3)
                block++;
        }

        if (block == 16)
        {
// ****** START TRANSFORM ******
for (i = 16; i < 80; i++) {
    temp = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
    w[i] = (temp << 1) | (temp >>> 31);
}

A = h0;
B = h1;
C = h2;
D = h3;
E = h4;

// round 1
E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[ 0] + 0x5A827999; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[ 1] + 0x5A827999; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[ 2] + 0x5A827999; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[ 3] + 0x5A827999; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[ 4] + 0x5A827999; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[ 5] + 0x5A827999; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[ 6] + 0x5A827999; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[ 7] + 0x5A827999; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[ 8] + 0x5A827999; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[ 9] + 0x5A827999; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[10] + 0x5A827999; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[11] + 0x5A827999; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[12] + 0x5A827999; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[13] + 0x5A827999; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[14] + 0x5A827999; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (D ^ (B & (C ^ D))) + w[15] + 0x5A827999; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (C ^ (A & (B ^ C))) + w[16] + 0x5A827999; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (B ^ (E & (A ^ B))) + w[17] + 0x5A827999; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (A ^ (D & (E ^ A))) + w[18] + 0x5A827999; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (E ^ (C & (D ^ E))) + w[19] + 0x5A827999; C = (C << 30) | (C >>> -30);

// round 2
E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1; C = (C << 30) | (C >>> -30);

// round 3
E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[40] + 0x8F1BBCDC; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[41] + 0x8F1BBCDC; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[42] + 0x8F1BBCDC; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[43] + 0x8F1BBCDC; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[44] + 0x8F1BBCDC; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[45] + 0x8F1BBCDC; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[46] + 0x8F1BBCDC; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[47] + 0x8F1BBCDC; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[48] + 0x8F1BBCDC; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[49] + 0x8F1BBCDC; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[50] + 0x8F1BBCDC; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[51] + 0x8F1BBCDC; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[52] + 0x8F1BBCDC; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[53] + 0x8F1BBCDC; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[54] + 0x8F1BBCDC; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + ((B & C) | (D & (B | C))) + w[55] + 0x8F1BBCDC; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + ((A & B) | (C & (A | B))) + w[56] + 0x8F1BBCDC; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + ((E & A) | (B & (E | A))) + w[57] + 0x8F1BBCDC; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + ((D & E) | (A & (D | E))) + w[58] + 0x8F1BBCDC; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + ((C & D) | (E & (C | D))) + w[59] + 0x8F1BBCDC; C = (C << 30) | (C >>> -30);

// round 4
E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6; C = (C << 30) | (C >>> -30);

E += ((A << 5) | (A >>> -5)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6; B = (B << 30) | (B >>> -30);
D += ((E << 5) | (E >>> -5)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6; A = (A << 30) | (A >>> -30);
C += ((D << 5) | (D >>> -5)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6; E = (E << 30) | (E >>> -30);
B += ((C << 5) | (C >>> -5)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6; D = (D << 30) | (D >>> -30);
A += ((B << 5) | (B >>> -5)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6; C = (C << 30) | (C >>> -30);

h0 += A;
h1 += B;
h2 += C;
h3 += D;
h4 += E;
// ******* END TRANSFORM *******
            block = 0;
        }
    }
}

final function Finish()
{
    local array<byte> pad;

    if (byteCount % 64 < 56)
        pad.Length = 64 - byteCount % 64;
    else
        pad.Length = 128 - byteCount % 64;

    pad[0] = 0x80;
    pad[pad.Length - 5] = (byteCount >>> 29);
    pad[pad.Length - 4] = (byteCount >>> 21);
    pad[pad.Length - 3] = (byteCount >>> 13);
    pad[pad.Length - 2] = (byteCount >>> 5);
    pad[pad.Length - 1] = (byteCount << 3);

    Update(pad);

    // Just a safeguard - the previous Update() should trigger a transform.
    if (block == 0)
        digested = true;
}

// Just an auxillary function to get the digested hash.
// Any coder worth their salt should know how to merge with Final() to improve efficiency...
final function String GetHash()
{
    if (!digested)
        return "";

    return IntToHex(h0)@IntToHex(h1)@IntToHex(h2)@IntToHex(h3)@IntToHex(h4);
}

static final function string IntToHex(int n)
{
    const hex = "0123456789ABCDEF";
    local string s;
    local int i;

    for (i = 0; i < 8; i++)
    {
        s = mid(hex, n & 0xf, 1) $ s;
        n = n >>> 4;
    }

    return s;
}

function int Main(string Params)
{
    local int i;
    local array<byte> b;

    b.Length = 3;
    log("SHA-1 Test Program");
    log("test: 'abc'");
    b[0] = 0x61;
    b[1] = 0x62;
    b[2] = 0x63;
    Init();
    Update(b);
    Finish();
    log(""$GetHash());

    b.Length = 1000000;
    log("SHA-1 Test Program");
    log("test: 1 million rounds of 'a'");
    for (i = 0; i < 1000000; i++)
        b[i] = 0x61;
    log ("done filling");
    Init();
    Update(b);
    Finish();
    log(""$GetHash());
}

I made it into a commandlet so all you have to do is compile-n-go to test it out.

Unfourtunately, the real bottleneck is the transformation itself, so there aren't any real measurable performance gains from having a contextd version. If anyone finds anything that can be optimized, please point it out and we'll see if this code can go any faster. For now, if you're using this for anything larger than 1MB for real time applications I'd recommend you use a reduced rounds implementation (which would also reduce the secureness of the resulting hash).


Category Custom Class
Category Algorithm

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

FAQs

Help Desk

Mapping Topics

Mapping Lessons

UnrealEd Interface

UnrealScript Topics

UnrealScript Lessons

Making Mods

Class Tree

Modeling Topics

Chongqing Page

Log In