Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HMAC Algorithm (SHA256) in Classic ASP VBScript

I'm trying to write an HMAC function in Classic ASP using SHA256 as the hash. I thought I got it right, but the results aren't the same for the examples listed on the Wikipedia page. I've seen several examples of people using HMAC SHA256 in Classic ASP by including a WSC. This is not what I want to do.

Here's the function:

Public Function HMAC_SHA256(prmKey, prmData)
    Dim theKey : theKey = prmKey
    Dim Block_Size, O_Pad, I_Pad
    Block_Size = 64
    O_Pad = 92 'HEX: 5c'
    I_Pad = 54 'HEX: 36'

    Dim iter, iter2
    If Len(theKey) < Block_Size Then
        For iter = 1 to Block_Size - Len(theKey)
            theKey = theKey & chr(0)
        Next
    ElseIf Len(theKey) > Block_Size Then
        theKey = SHA256(theKey)
    End If

    Dim o_key_pad : o_key_pad = ""
    Dim i_key_pad : i_key_pad = ""
    For iter = 1 to Block_Size
        o_key_pad = o_key_pad & Chr(Asc(Mid(theKey,iter,1)) xor O_Pad)
        i_key_pad = i_key_pad & Chr(Asc(Mid(theKey,iter,1)) xor I_Pad)
    Next

    HMAC_SHA256 = SHA256(o_key_pad & SHA256(i_key_pad & prmData))
End Function

And here's the pseudocode from Wikipedia's HMAC:

function hmac (key, message)
    if (length(key) > blocksize) then
        key = hash(key) // keys longer than blocksize are shortened
    end if
    if (length(key) < blocksize) then
        key = key ∥ [0x00 * (blocksize - length(key))] // keys shorter than blocksize are zero-padded (where ∥ is concatenation)
    end if

    o_key_pad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function
    i_key_pad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR)

    return hash(o_key_pad ∥ hash(i_key_pad ∥ message)) // Where ∥ is concatenation
end function

I can't for the life of me determine what's wrong here. I imagine someone's going to tell me it's impossible in ASP and that's why there are so many suggestions to use the WSC. If this is the case, can someone explain why it's impossible? I don't see anything that should make this impossible.

Thanks!

EDIT: I've made sure the SHA256 function I use is giving the correct hashes, so it's nothing to do with that. I've also tried UCase'ing the results of the SHA256 function, and no dice.

EDIT2: Added example from wiki page.

like image 362
Sloan Reynolds Avatar asked Apr 29 '26 06:04

Sloan Reynolds


1 Answers

Well, I once implemented TEA (tiny encryption algorithm) in classic ASP and had similar problems. In my case, the root cause was, that ASP saves the strings you are concatenating (& char) again as UTF-16 and so the offsets did not always match up where I expected them.

I don't know if this applies to your use case, since I had to work with UTF-8 special characters.

My solution was to work with an array of longs, that I could target properly with my binary functions. Here are the functions to work with this array, hoping they are helpful for you.

    '*******************************************************************************
    ' getArrayFromInputString (FUNCTION)
    '
    ' PARAMETERS:
    ' (In) - s_source - Source string (format is defined by n_options)
    ' (In) - n_blocksize - Blocksize, which is corrected by padding
    ' (In) - n_options - Options using follobing bits:
    '        1: string is in HEX format (e.g. DFD14DAFD9C555C07FEB8F3DA90DEA27)

    ' RETURN VALUE:
    ' long array
    '
    ' DESCRIPTION:
    ' allows to import strings in various formats for all input functions
    '*******************************************************************************
    private function getArrayFromInputString(s_source, n_blocksize, n_options)
        ' n_options:

        dim a_out, s_padded_string

        if (n_options AND 1) = 1 then
            s_padded_string = padString(s_source, n_blocksize * 2, "0")
            a_out = convertHexStringToArray(s_padded_string)
        else
            if b_unicode_ then
                s_padded_string = padString(s_source, int(n_blocksize / 2), " ")
                a_out = convertStringToArray_Unicode(s_padded_string)
            else    
                s_padded_string = padString(s_source, n_blocksize, " ")
                a_out = convertStringToArray(s_padded_string)
            end if
        end if

        getArrayFromInputString = a_out
    end function


    '*******************************************************************************
    ' convertStringToArray (FUNCTION)
    '
    ' PARAMETERS:
    ' (In) - s_source - Source string to build the array from
    '                   length MUST be in multiples of 4!
    '
    ' RETURN VALUE:
    ' Array of type Long - Length is 4 times smaller than the string length
    '
    ' DESCRIPTION:
    ' Blocks of four characters are calculated into one Long entry of the result array
    '*******************************************************************************
    private function convertStringToArray(s_source) ' returns long array
        dim a_out, n_index, n_length, n_temp
        dim n_array_index, n_nibble

        n_length = len(s_source)
        redim a_out(int(n_length / 4))

        for n_index=0 to n_length - 1
            n_temp = asc(mid(s_source, n_index + 1, 1))
            n_array_index = int(n_index / 4)
            n_nibble = n_index MOD 4

            a_out(n_array_index) = AddUnsigned(a_out(n_array_index), LShift(n_temp, (3 - n_nibble) * 8))
        next
        convertStringToArray = a_out
    end function

    '*******************************************************************************
    ' convertHexStringToArray (unicode version)
    private function convertStringToArray_Unicode(s_source) ' returns long array
        dim a_out, n_index, n_length, n_temp
        dim n_array_index, n_nibble

        n_length = len(s_source)
        redim a_out(int(n_length / 2))

        for n_index=0 to n_length - 1
            n_temp = ascw(mid(s_source, n_index + 1, 1))

            n_array_index = int(n_index / 2)
            n_nibble = (n_index MOD 2)

            a_out(n_array_index) = AddUnsigned(a_out(n_array_index), LShift(n_temp, (1 - n_nibble) * 16))
        next
        convertStringToArray_Unicode = a_out
    end function

    '*******************************************************************************
    ' convertHexStringToArray (FUNCTION)
    '
    ' PARAMETERS:
    ' (In) - s_source - Source string in hex format, e.g. "EFCE016503CDDB53"
    '                   length MUST be in multiples of 8!
    '
    ' RETURN VALUE:
    ' Array of type Long - Length is 8 times smaller than the string length
    '
    ' DESCRIPTION:
    ' Blocks of eight characters are calculated into one Long entry of the result array
    '*******************************************************************************
    private function convertHexStringToArray(s_source) ' returns long array
        dim a_out, n_index, n_length, n_temp
        dim n_array_index, n_nibble

        n_length = len(s_source)
        redim a_out(int(n_length / 8))

        for n_index=0 to n_length - 1 step 2
            n_temp = CInt("&H" & mid(s_source, n_index + 1, 2))
            n_array_index = int(n_index / 8)
            n_nibble = int((n_index MOD 8) / 2)

            a_out(n_array_index) = AddUnsigned(a_out(n_array_index), LShift(n_temp, (3 - n_nibble) * 8))
        next
        convertHexStringToArray = a_out
    end function


    '*******************************************************************************
    ' padString (FUNCTION)
    '
    ' PARAMETERS:
    ' (In) - s_source
    ' (In) - n_blocksize
    ' (In) - s_padding_char
    '
    ' RETURN VALUE:
    ' String - padded source string
    '
    ' DESCRIPTION:
    ' ensure, that the plaintext is multiples of n_blocksize bytes long, the needed amount of s_padding_char is applied
    '*******************************************************************************
    private function padString(s_source, n_blocksize, s_padding_char)
        dim s_out, n_length, n_padding, n_index

        s_out = s_source
        n_length = len(s_source)
        if n_length MOD n_blocksize>0 then
            n_padding = n_blocksize - n_length MOD n_blocksize
            for n_index=1 to n_padding
                s_out = s_out & left(s_padding_char, 1)
            next
        end if

        padString = s_out
    end function

    '*******************************************************************************
    ' printArray (FUNCTION)
    '
    ' PARAMETERS:
    ' (In) - s_prefix - just a string to be written in front for distinction of multiple arrays
    ' (In) - a_data - long array to print out
    '
    ' RETURN VALUE:
    ' none
    '
    ' DESCRIPTION:
    ' debug output function
    '*******************************************************************************
    private function printArray(s_prefix, a_data)
        dim n_index
        for n_index=0 to UBound(a_data) - 1
            Response.Write "<p>" & s_prefix & a_data(n_index) & " - " & getHex(a_data(n_index)) & "</p>" & vbNewline
        next
    end function

    '*******************************************************************************
    ' Some more little helper functions
    '*******************************************************************************
    private function getHex(n_value)
        getHex = Right("00000000" & Hex(n_value), 8)
    end function

    private function getStringFromLong(n_value)
        getStringFromLong = _
            Chr(RShift(n_value, 24) AND &HFF) & _
            Chr(RShift(n_value, 16) AND &HFF) & _
            Chr(RShift(n_value, 8) AND &HFF) & _
            Chr(n_value AND &HFF)
    end function

    private function getStringFromLong_Unicode(n_value)
        dim s_temp

        s_temp = getHex(n_value)
        getStringFromLong_Unicode = _
            ChrW(int("&H" & mid(s_temp, 1, 4))) & _
            ChrW(int("&H" & mid(s_temp, 5, 4)))
    end function
like image 160
gpinkas Avatar answered May 01 '26 05:05

gpinkas