Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

HMAC: The CNG way

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin/PBCC HMAC: The CNG way

    Creating a HMAC using Microsoft's CryptoAPI is not straightforward. When I wrote SecureHash.sll I hid the complexity from the user requiring only a HMAC key as an additional parameter to hashing only. Creating a HMAC using 'Cryptography API: Next Generation.' (CNG) with Windows Vista and above is almost as easy.

    Of all the hashing algorithms available I find myself using SHA256 the most so the following function, Hash(), is SHA256 hard-wired; but can be easily adapted for other hash algorithms.

    The first pass of Hash() requires that the second parameter, phHash, is zero - this ensures that a hash object is created. During the first pass the first or only message, sText, is hashed. If Final is false then further messages are expected and they will be 'hashed into' the hash object. If Final is true then the last or only message is 'hashed into' the hash object and the hash is finished; the hash object is destroyed and phHash is reset in preparation of a new sequence of Hash(), if any.

    Most of my hashing work remains in the binary domain so Hash() outputs binary; when Final is true.

    In the following example the first part uses Hash() for hashing only and we process three messages and then process a concatenation of the three messages giving, of course, the same hash result.

    Concatenation is not always possible - we may be reading a file via a buffered input - we cannot hash a second buffer (message) until the first buffer (message) is read, and so on.

    The second part uses Hash() to create a HMAC and, again, we process three messages and then a concatenation.

    The HMAC is 'forced' by using BCRYPT_ALG_HANDLE_HMAC_FLAG in the BCryptOpenAlgorithmProvider API and the secret HMAC key is introduced in BCryptCreateHash.

    This is how it should have been done in CryptoAPI.

    The concatenation results have been verified using other software.

    I wish that I had gotten into CNG sooner. At first it looked more daunting than CryptoAPI but it turns out to be easier to use.

    Code:
    #Compile Exe
    #Dim All
    #Register None
    
    #Include "WIN32API.INC"
    
    Function PBMain() As Long
    Local hHashAlg, hHMACAlg, phHash As Dword
    Local sText, sBinHash, msg As String
    
      ' ****** Hash ******
      
      BCryptOpenAlgorithmProvider( hHashAlg, "SHA256"$$, $$Nul, 0)
      
      Hash( hHashAlg, phHash, "Compile", %False )
      Hash( hHashAlg, phHash, " without", %False )
      sBinHash = Hash( hHashAlg, phHash, " compromise", %True )
       
      msg = Bin2Hex( sBinHash ) + $CrLf
      
      sBinHash = Hash( hHashAlg, phHash, "Compile without compromise", %True )
      
      msg += Bin2Hex( sBinHash )
      
      MsgBox msg
      
      BCryptCloseAlgorithmProvider( hHashAlg, 0 )
      
      ' ******
      
      ' ****** HMAC ******
      
      BCryptOpenAlgorithmProvider( hHMACAlg, "SHA256"$$, $$Nul, %BCRYPT_ALG_HANDLE_HMAC_FLAG)
      
      Hash( hHMACAlg, phHash, "Compile", %False, "Secret" )
      Hash( hHMACAlg, phHash, " without", %False )
      sBinHash = Hash( hHMACAlg, phHash, " compromise", %True )
      
      msg = Bin2Hex( sBinHash ) + $CrLf
      
      sBinHash = Hash( hHMACAlg, phHash, "Compile without compromise", %True, "Secret" )
      
      msg += Bin2Hex( sBinHash )
      
      MsgBox msg
      
      BCryptCloseAlgorithmProvider( hHMACAlg, 0 )
      
      ' ******
         
    End Function
    
    Function Hash( ByVal hAlg As Dword, phHash As Dword, sText As String, ByVal Final As Long, Opt pbSecret As String ) As String
    ' Input
    ' hAlg As given by BCryptOpenAlgorithmProvider
    ' sText Is message To be hashed
    ' Final Is false If further messages are To be 'hashed In' Else true.
    ' pbSecret Is secret HMAC key
    ' Input/Output
    ' phHash Is zero On first pass Of Hash() And As given by BCryptCreateHash On second And subsequent passes, if any
      
    Local sBinHash As String * 32 ' Hard wired At 32 For SHA256
    
      If IsFalse phHash Then
        If IsMissing( pbSecret ) Then
          BCryptCreateHash( hAlg, phHash, %Null, 0, 0, 0, 0 )
        Else
          BCryptCreateHash( hAlg, phHash, ByVal %Null, 0, StrPtr( pbSecret ), Len( pbSecret ), 0 )
        End If
      End If
    
      BCryptHashData( phHash, StrPtr( sText ), Len( sText ), 0 )
      
      If IsTrue Final Then
        BCryptFinishHash( phHash, VarPtr( sBinHash ), 32, 0 )
        BCryptDestroyHash( phHash )
        Reset phHash ' Ensures that Next time Hash() Is used a hash Object Is created
        Function = sBinHash
      End If
      
    End Function
    
    Function Bin2Hex( ByVal strSource As String ) As String
    ' Input Binary String
    ' Output Hex String
    
    Local i As Long
    Local ptrSource As Byte Ptr
    Local strDestination As String
    Local ptrDestination As String Ptr
    
      ptrSource = StrPtr( strSource )
      strDestination = Space$( 2 * Len( strSource ) )
      ptrDestination = StrPtr(strDestination)
      
      For i = 0 To Len( strSource ) - 1
        Poke$ ptrDestination, Hex$( @ptrSource[i], 2 )
        ptrDestination += 2
      Next
      
      Function = strDestination
      
    End Function
    Last edited by David Roberts; 21 Jan 2015, 12:35 AM.

  • #2
    It is worth noting that if we use BCRYPT_ALG_HANDLE_HMAC_FLAG and then fail to supply a HMAC key so that IsMissing( pbSecret ) is true then the resulting hash will be as if we had supplied an empty string for the HMAC key.

    On the other hand if we do not use BCRYPT_ALG_HANDLE_HMAC_FLAG and supply a HMAC key anyway it will, surprisingly, be read and BCryptCreateHash will fail.

    BCryptOpenAlgorithmProvider is executed without Hash() so that more than one hash sequence can exist at the same time. For example Hash( hHashAlg, phHash1, ... ) and Hash( hHashAlg, phHash2, ...) and so on are acceptable.

    Comment


    • #3
      I didn't particularly want to but I used SHA1 recently. The following Hash() function will handle any hash.

      The third parameter, sText, has been replaced by two parameters: lAddress and lLength where lAddress is the address of the message and lLength is the length of the message. Strings are now handled with StrPtr() and Len(). On the other hand we may have a buffer created with one of the memory allocation procedures.

      We can now use "MD5"$$, "SHA1"$$, "SHA256"$$, "SHA384"$$ and "SHA512"$$.

      The concatenation results for the above hashes have been verified using other software.

      The following uses "SHA1"$$.

      Code:
      #Compile Exe
      #Dim All
      #Register None
      
      #Include "WIN32API.INC"
      
      Function PBMain() As Long
      Local hHashAlg, hHMACAlg, phHash As Dword
      Local sBinHash, msg As String
      Local a,b,c,d As String
      
      a = "Compile"
      b = " without"
      c = " compromise"
      d = "Compile without compromise"
      
        ' ****** Hash ******
        
        BCryptOpenAlgorithmProvider( hHashAlg, "SHA1"$$, $$Nul, 0)
        
        Hash( hHashAlg, phHash, StrPtr(a), Len(a), %False )
        Hash( hHashAlg, phHash, StrPtr(b), Len(b), %False )
        sBinHash = Hash( hHashAlg, phHash, StrPtr(c), Len(c), %True )
         
        msg = Bin2Hex( sBinHash ) + $CrLf
        
        sBinHash = Hash( hHashAlg, phHash, StrPtr(d), Len(d), %True )
        
        msg += Bin2Hex( sBinHash )
        
        MsgBox msg
        
        BCryptCloseAlgorithmProvider( hHashAlg, 0 )
        
        ' ******
        
        ' ****** HMAC ******
        
        BCryptOpenAlgorithmProvider( hHMACAlg, "SHA1"$$, $$Nul, %BCRYPT_ALG_HANDLE_HMAC_FLAG)
        
        Hash( hHMACAlg, phHash, StrPtr(a), Len(a), %False, "Secret" )
        Hash( hHMACAlg, phHash, StrPtr(b), Len(b), %False )
        sBinHash = Hash( hHMACAlg, phHash, StrPtr(c), Len(c), %True )
        
        msg = Bin2Hex( sBinHash ) + $CrLf
        
        sBinHash = Hash( hHMACAlg, phHash, StrPtr(d), Len(d), %True, "Secret" )
        
        msg += Bin2Hex( sBinHash )
        
        MsgBox msg
        
        BCryptCloseAlgorithmProvider( hHMACAlg, 0 )
        
        ' ******
           
      End Function
      
      Function Hash( ByVal hAlg As Dword, phHash As Dword, ByVal lAddress As Long Ptr, ByVal lLength As Long, ByVal Final As Long, Opt pbSecret As String ) As String
      ' Input
      ' hAlg As given by BCryptOpenAlgorithmProvider
      ' lAddress Is address Of message To be hashed
      ' lLength Is length Of message
      ' Final Is false If further messages are To be 'hashed In' Else true.
      ' pbSecret Is secret HMAC key
      ' Input/Output
      ' phHash Is zero On first pass Of Hash() And As given by BCryptCreateHash On second And subsequent passes
      
        If IsFalse phHash Then
          If IsMissing( pbSecret ) Then
            BCryptCreateHash( hAlg, phHash, %Null, 0, 0, 0, 0 )
          Else
            BCryptCreateHash( hAlg, phHash, ByVal %Null, 0, StrPtr( pbSecret ), Len( pbSecret ), 0 )
          End If
        End If
      
        BCryptHashData( phHash, lAddress, lLength, 0 )
        
        If IsTrue Final Then
          Local lHashLength, lResult As Long
          Local sBinHash As String
          BCryptGetProperty( hAlg, $$BCRYPT_HASH_LENGTH, VarPtr( lHashLength ), 4, lResult, 0 )
          sBinHash = Space$( lHashLength )
          BCryptFinishHash( phHash, StrPtr( sBinHash ), lHashLength, 0 )
          BCryptDestroyHash( phHash )
          Reset phHash ' Ensures that Next time Hash() Is used a hash Object Is created
          Function = sBinHash
        End If
        
      End Function
      
      Function Bin2Hex( ByVal strSource As String ) As String
      ' Input Binary String
      ' Output Hex String
      
      Local i As Long
      Local ptrSource As Byte Ptr
      Local strDestination As String
      Local ptrDestination As String Ptr
      
        ptrSource = StrPtr( strSource )
        strDestination = Space$( 2 * Len( strSource ) )
        ptrDestination = StrPtr(strDestination)
        
        For i = 0 To Len( strSource ) - 1
          Poke$ ptrDestination, Hex$( @ptrSource[i], 2 )
          ptrDestination += 2
        Next
        
        Function = strDestination
        
      End Function

      Comment


      • #4
        Hi David,

        I am in the process of teaching myself about hash and crypto stuff. Your examples have been most instructive.
        Thanks!!!

        Comment


        • #5
          Comments here.

          Comment

          Working...
          X