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

AES 128/192/256 in XP SP3

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

  • David Roberts
    replied
    With all the copy and pasting done in the function MSEncDec3 source code the WinErr position numbers got out of sync. I have corrected the above. If you have copied the source code then you can correct them yourself. I got 10 through 220. Hopefully, you'll never see them.

    Leave a comment:


  • David Roberts
    replied
    I wanted EncDecMode as an optional parameter but kept getting GPFs. I needed to get my head into gear and have now done so.

    Add 'EncDecMode as Long' to the list of Local Longs. EncDecMode is no longer 'coming in' - EDMode is coming in now.

    Rewrite MSEncDec3's declaration as
    Code:
    Function MSEncDec3( ByVal sInFile As String, ByVal WhichAlgorithm As Long, ByVal sMasterKey As String, _
                        Opt EDMode As Long, sInitialValues As String ) As String
    and add after 'Function = "OK" ' Look on the bright side <smile>'
    Code:
    If IsFalse VarPtr(EDMode) Then
      EncDecMode = %True
    Else
      EncDecMode = EDMode
    End If
    We can now use something this:
    Code:
    sStatus = MSEncDec3( sFile, %CALG_AES_256, sMasterKey )
    and authenticate will be the default.

    If EncDecMode = %True is changed to EncDecMode = %False then the default will be a bare bones encryption.

    A revised library has been uploaded and that, of course , defaults to authentication.

    Leave a comment:


  • David Roberts
    replied
    MSEncDec3

    Code:
    #Compile Exe "MSEncDec3"
    #Dim All
    #Register None
    #Tools Off
    
    %NOMMIDS = 1
    %NOGDI = 1
    
    #Include "win32api.inc"
    #Include "wincrypt.inc"
    
    %BufferSize = 262144 ' 256KB
    %Bin = 1
    %Hex = 0
    %SaltLen = 16 ' ie 128 bit
    
    Macro SysErrMsg( API, Number ) = MessageBox 0, SystemErrorMessage( WinErr, API, FuncName$, Number ), "MSEncDec3", %MB_ICONERROR + %MB_TOPMOST
    
    Type HMAC_Info
      HashAlgid As Word
      dummy As String * 6
    End Type
    
    Function PBMain
    Local sFile, sMasterKey, IV, sStatus As String
    
    
      sFile = "Whatever" ' <--------------- [COLOR="Red"]Your filepath here[/COLOR]
            
      sMasterKey = "2E664237276E5BC4FA7DA0FCBE749F921700BF615333886A6ADAFB20F697B543"
      ' Wholly hexadecimal and even numbered so will be treated as a binary string
        
      ' If either of following are uncommented then use [2] otherwise [1]
      'IV ="29B755EB595E19AD72B7C4275CA227D6"
      'IV = ""
      
      sStatus = MSEncDec3( sFile, %CALG_AES_256, sMasterKey, %True ) ' [1]
      'sStatus = MSEncDec3( sFile, %CALG_AES_256, sMasterkey, %True, IV ) ' [2]
       
      If sStatus = "OK" Then
        MessageBox 0, "File encrypted", "MSEncDec3", %MB_ICONINFORMATION + %MB_TOPMOST
       Else
        MessageBox 0, "Error encrypting file", "MSEncDec3", %MB_ICONERROR + %MB_TOPMOST
        GoTo terminate
      End If
      
      sFile = sFile + ".enc"
      
      sStatus = MSEncDec3( sFile, %CALG_AES_256, sMasterKey, %True )
      
      If sStatus = "OK" Then
        MessageBox 0, "File decrypted", "MSEncDec3", %MB_ICONINFORMATION + %MB_TOPMOST
      Else
        MessageBox 0, "Error decrypting file", "MSEncDec3", %MB_ICONERROR + %MB_TOPMOST
      End If
    
    Terminate:  
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        Waitkey$
      #EndIf
      
    End Function
    
    ' ******************************************************************************************************
    
    Function MSEncDec3( ByVal sInFile As String, ByVal WhichAlgorithm As Long, ByVal sMasterKey As String, _
                        ByVal EncDecMode As Long, Opt sInitialValues As String ) As String
                       
    Local WinErr, hIn, hOut, i, NoOfBytes, HashLen As Long
    Local Dummy, sOutFile, pbdata, HashValue, PlainTextHash, SaltAES, SaltHMAC As String
    Local WhichHash, hProv, hEncDecHash, hEncDecKey, hHMACHash, hHMACKey As Long, BlockSize As Dword
    Local pdwDatalen As Dword
    Local DeriveKeydwFlags, IsInputEncrypted, FileLen As Dword, FinalSection, SectionCount, dwBuflen, Final As Long
    Dim IV() As Byte
    Dim Salt() As Byte
    Dim HMACInfo As HMAC_Info
    
      Function = "OK" ' Look on the bright side <smile>
      
      ' Set up input out files
      If Dir$( sInFile ) = "" Then
        MessageBox 0, "Unable to find " + sInfile, "MSEncDec3", %MB_ICONWARNING + %MB_TOPMOST
         Function = ""
        GoTo TidyUp
      End If
    
      hIn = FreeFile
      Open sInFile For Binary Lock Shared As hIn
      If Err Then
        MessageBox 0, "Error" + Str$(Err) + " opening " + sInFile, "MSEncDec3", %MB_ICONERROR + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      FileLen = Lof (hIn)
      If Filelen = 0 Then
        MessageBox 0, "Input file" + $CrLf + $CrLf + sInFile + $CrLf + $CrLf + "is a zero byte file", "MSEncDec3", %MB_ICONWARNING + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      
      If LCase$( Right$( sInFile, 4 ) ) <> ".enc" Then
        sOutFile = sInFile & ".enc"
        ' eg e:\folder\myapp.exe ==> e:\folder\myapp.exe.enc
        IsInputEncrypted = %False
      Else
        sOutFile = Left$( sInFile, Len( sInFile ) - 4 )
        ' eg e:\folder\myapp.exe.enc ==> e:\folder\myapp.exe
        IsInputEncrypted = %True
      End If
      If Dir$( sOutFile ) <> "" Then
        Kill sOutFile
      End If
      
      hOut = FreeFile
      Open sOutFile For Binary As hOut
      If Err Then
        MessageBox 0, "Error" + Str$(Err) + " opening " + sOutFile, "MSEncDec3", %MB_ICONERROR + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      
      ' Are we going to use sMasterKey as ascii or binary?
      If ( sMasterKey <> "" ) And ( Len( sMasterKey ) Mod 2 = 0 ) And  IsTrue Hex( sMasterKey ) Then
        ' Treat as a binary string
        NoOfBytes = Len( sMasterKey )/2
        For i = 0 To NoOfBytes - 1
          Dummy = Dummy + Chr$( Val( "&H" + Mid$( sMasterKey, 2*i+1, 2 ) ) )
        Next
        sMasterKey = Dummy
        Reset Dummy
      End If
      
      ' Select hash algoritim according to chosen encryption algorithm
      ' You could add others, but why? Don't use stream ciphers - BlockSize = 0 is no longer checked for.
      Select Case WhichAlgorithm
        Case %CALG_AES_128
          WhichHash = %CALG_SHA_256
          HashLen = 32
          DeriveKeydwFlags = Mak(Dword, 0, 128) Or %CRYPT_EXPORTABLE
        Case %CALG_AES_192
          WhichHash = %CALG_SHA_384
          HashLen = 48
          DeriveKeydwFlags = Mak(Dword, 0, 192) Or %CRYPT_EXPORTABLE
        Case %CALG_AES_256
          WhichHash = %CALG_SHA_512
          HashLen = 64
          DeriveKeydwFlags = Mak(Dword, 0, 256) Or %CRYPT_EXPORTABLE
        Case Else
          MessageBox 0, "I do not recognise the given encryption algorithm", "MSEncDec3", %MB_ICONWARNING + %MB_TOPMOST
          Function = ""
          GoTo TidyUp
      End Select
      
      hProv = crGetDefaultRSAHandle
      If hProv = 0 Then ' We have already been advised of a failure
        Function = ""
        Exit Function
      End If
      
      ' Execute the following two commands in any event for hEncDecKey
      If CryptCreateHash( hProv, WhichHash, 0, 0, hEncDecHash ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 10 )
        Function = ""
        GoTo TidyUp
      End If
      ' Hash in sMasterKey
      If CryptHashData( hEncDecHash, ByVal StrPtr(sMasterKey), Len(sMasterKey), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptHasData", 20 )
        Function = ""
        GoTo TidyUp
      End If
      
      If IsTrue EncDecMode Then ' authenticate
      
        ReDim Salt( %SaltLen - 1 ) As Byte
        
        If IsFalse IsInputEncrypted Then ' so encrypt
        
          ' Set SaltAES
          If CryptGenRandom( hProv, %SaltLen, Salt( 0 ) )= 0 Then
            WinErr = GetLastError : SysErrMsg( "CryptGenRandom", 30 )
            Function = ""
            GoTo TidyUp
          Else
            SaltAES = Peek$( VarPtr( Salt( 0 ) ), %SaltLen )
          End If
          
          ' Set SaltHMAC
          Reset Salt()
          If CryptGenRandom( hProv, %SaltLen, Salt( 0 ) )= 0 Then
            WinErr = GetLastError : SysErrMsg( "CryptGenRandom", 40 )
            Function = ""
            GoTo TidyUp
          Else
            SaltHMAC = Peek$( VarPtr( Salt( 0 ) ), %SaltLen )
          End If
                
          Put$ hOut, Nul$(HashLen)
          Put$ hOut, SaltAES
          Put$ hOut, SaltHMAC
          
        Else ' decrypt
        
          Get$ hIn, HashLen, PlainTextHash
          Get$ hIn, %SaltLen, SaltAES
          Get$ hIn, %Saltlen, SaltHMAC
          
        End If ' IsFalse IsInputEncrypted
        
        ' Hash in SaltAES with hEncDecHash
        If CryptHashData( hEncDecHash, ByVal StrPtr(SaltAES), Len(SaltAES), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptHasData", 50 )
          Function = ""
          GoTo TidyUp
        End If
        
        ' Now hash 1000 times
        For i = 1 To 1000
          If GetHash( HashValue, hEncDecHash, %Bin ) = %False Then GoTo TidyUp
          ' hEncDecHash is rendered useless during Gethash so recreate
          If CryptCreateHash( hProv, WhichHash, 0, 0, hEncDecHash ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 60 )
            Function = ""
            GoTo TidyUp
          End If
          ' Hash in Hashvalue
          If CryptHashData( hEncDecHash, ByVal StrPtr(HashValue), Len(Hashvalue), 0 ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptHasData", 70 )
            Function = ""
            GoTo TidyUp
          End If
        Next
            
        ' Now derive hEncDecKey
        If CryptDeriveKey( hProv, WhichAlgorithm, hEncDecHash, DeriveKeydwFlags, hEncDecKey ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 80 )
          Function = ""
          GoTo TidyUp
        End If 
        
        ' Now get hHMACKey
        If CryptCreateHash( hProv, WhichHash, 0, 0, hHMACHash ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 90 )
          Function = ""
          GoTo TidyUp
        End If
        ' Hash in sMasterkey
        If CryptHashData( hHMACHash, ByVal StrPtr( sMasterKey ), Len( sMasterKey ), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptHasData", 100 )
          Function = ""
          GoTo TidyUp
        End If
        ' Hash in SaltHMAC
        If CryptHashData( hHMACHash, ByVal StrPtr(SaltHMAC), Len(SaltHMAC), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptHasData", 110 )
          Function = ""
          GoTo TidyUp
        End If
        
        ' Now hash 1000 times
        For i = 1 To 1000
          If GetHash( HashValue, hHMACHash, %Bin ) = %False Then GoTo TidyUp
          ' hHMACHash is rendered useless during Gethash so recreate
          If CryptCreateHash( hProv, WhichHash, 0, 0, hHMACHash ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 120 )
            Function = ""
            GoTo TidyUp
          End If
          ' Hash in Hashvalue
          If CryptHashData( hHMACHash, ByVal StrPtr(HashValue), Len(Hashvalue), 0 ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptHasData", 130 )
            Function = ""
            GoTo TidyUp
          End If
        Next
        
        ' Now derive hHMACKey
        If CryptDeriveKey( hProv, WhichAlgorithm, hHMACHash,  DeriveKeydwFlags, hHMACKey ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 140 )
          Function = ""
          GoTo TidyUp
        End If
        
        ' Now set up HMAC
        If CryptCreateHash( hProv, %CALG_HMAC, hHMACKey, 0, hHMACHash ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 150 )
          Function = ""
          GoTo TidyUp
        End If
        HMACInfo.HashAlgid = WhichHash
        If CryptSetHashParam( hHMACHash, %HP_HMAC_INFO, HMACInfo, 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptSetHashParam", 160 )
          Function = ""
          GoTo TidyUp
        End If
        
      Else ' EncDecMode is false ie no authentication
      
        ' Derive hEncDecKey
        If CryptDeriveKey( hProv, WhichAlgorithm, hEncDecHash, DeriveKeydwFlags, hEncDecKey ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 170 )
          Function = ""
          GoTo TidyUp
        End If
        
      End If ' IsTrue EncDecMode
            
      ' Get the encryption algorithms block size for IV - 16 for AES but you may have added other block ciphers. Why?
      If CryptGetKeyParam ( hEncDecKey, %KP_BLOCKLEN, BlockSize, SizeOf(BlockSize), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptGetKeyParam", 180 )
        Function = ""
        GoTo TidyUp
      End If
      
      BlockSize = BlockSize\8 ' In bytes
      
      ReDim IV(0 To BlockSize - 1 ) As Byte
      
      If IsFalse IsInputEncrypted Then
        
        If IsFalse VarPtr(sInitialValues) Then ' Optional IV not given so generate a random binary array IV and dump
        
          If CryptGenRandom( hProv, BlockSize, IV( 0 ) )= 0 Then
            WinErr = GetLastError : SysErrMsg( "CryptGenRandom", 190 )
            Function = ""
            GoTo TidyUp
          End If
          
        Else ' Optional IV given
        
          If ( sInitialValues <> "" ) Then
            If ( Len( sInitialValues ) Mod 2 = 0 ) And IsTrue Hex( sInitialValues ) Then
              If Len( sInitialValues ) < 2 * BlockSize Then
                sInitialValues = sInitialValues + Nul$( 2 * BlockSize - Len( sInitialValues ) )
              End If
              For i = 0 To BlockSize - 1
                IV(i) = Val( "&H" + Mid$( sInitialValues, 2*i+1, 2 ) )
              Next
            Else
              MessageBox 0, "Given IV is either not all hexadecimal or not even numbered", "MSEncDec3", %MB_ICONWARNING + %MB_TOPMOST
              Function = ""
              GoTo TidyUp
            End If
          End If
          
        End If ' IsFalse VarPtr(sInitialValues)
    
        
        ' If Optional IV given but was empty then we will fall through to here
        ' IV() will be empty, as created, effectively giving MS Default
        
        Put hOut, ,IV() ' dump IV before encrypting
          
      Else ' we are decrypting
             
        Get hIn, ,IV()
        
      End If ' IsFalse IsInputEncrypted
      
      ' OK, we have our IV so set the Initial Values to ours
      If CryptSetKeyParam( hEncDecKey, %KP_IV, IV( 0 ), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptSetKeyParam", 200 )
        Function = ""
        GoTo TidyUp
      End If
       
      dwBuflen = %BufferSize ' Initial buffer size
      pdwDatalen = dwBuflen ' prior to the Final section
      FinalSection = FileLen\dwBuflen
      If (FileLen Mod dwBuflen) > 0 Then Incr FinalSection ' Data of 3.5, say, buffers worth will have 4 sections
      
      Do
        Incr SectionCount
        Get$ hIn, dwBuflen, pbdata
        If SectionCount = FinalSection Then ' note the Final data & prepare for encryption padding
          pdwDatalen = Len(pbdata)
          If IsFalse IsInputEncrypted Then
            pbdata = pbdata + Space$(BlockSize - pdwDatalen Mod BlockSize) ' Provide for padding
            dwBuflen = Len(pbdata)
            ' Note that dwBuflen may be reduced from its initial size but it is not neccessary
            ' An increase is necessary if this pdwDatalen = initial buffer size  
          End If
          Final = %True
        End If
        ' hHMACHash will be zero if EncDecMode = 0
        If IsInputEncrypted Then
          If CryptDecrypt( hEncDecKey, hHMACHash, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptDecrypt", 2100 )
            Function = ""
            GoTo TidyUp
          End If
        Else
          If CryptEncrypt( hEncDecKey, hHMACHash, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen, dwBuflen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptEncrypt", 220 )
            Function = ""
            GoTo TidyUp 
          End If
        End If 
        If Final Then
          Put$ hOut, Left$( pbdata, pdwDatalen)
        Else
          Put$ hOut, pbdata
        End If
      Loop Until Final
      
      If EncDecMode <> 0 Then
        If GetHash( HashValue, hHMACHash, %Bin ) = %False Then GoTo TidyUp
        If IsFalse IsInputEncrypted Then
          Put hOut, 1, Hashvalue ' Dump the hash value to head of file
        Else
          If HashValue <> PlainTextHash Then ' Compare hash value with headers value
            MessageBox 0, "If the Master Key is correct then the deciphered" + $CrLf + _
            "file is not the same as the original file", "MSEncDec3 - authentication failed.", %MB_ICONERROR + %MB_TOPMOST
            Function = ""
            GoTo TidyUp
          End If
        End If
      End If
      
    TidyUp:  
      
      If hProv Then CryptReleaseContext hProv, 0
      If hEncDecHash Then CryptDestroyHash hEncDecHash
      If hEncDecKey Then CryptDestroyKey hEncDecKey
      If hHMACHash Then CryptDestroyHash hHMACHash
      If hHMACKey Then CryptDestroyKey hHMACKey
      Close hIn
      Close hOut
    
    End Function
    
    ' ********************************************************************************************************************
    
    Function crGetDefaultRSAHandle As Long
    Local hProv, WinErr             As Long
    
    If CryptAcquireContext( hProv, ByVal %Null, ByVal %Null, %PROV_RSA_AES, 0 ) = %False Then
      If CryptAcquireContext( hProv, ByVal %Null, $MS_ENH_RSA_AES_PROV, %PROV_RSA_AES, %CRYPT_NEWKEYSET ) = %False Then
         WinErr = GetLastError : SysErrMsg( "CryptAcquireContext", 10 )
         Function = %False
      Else
         Function = hProv
      End If
    Else
      Function = hProv
    End If
    End Function
    
    ' *****************************************************************************************************************
    
    Function GetHash( hashText As String, hHash As Long, ByVal Flag As Long ) As Long
    ' If input Flag = 1 (%Bin) then hashText output is Binary
    ' If input Flag = 0 (%Hex) then hashText output is Hex
    
    Local i, hashSize, WinErr As Long
    
    Dim aByte() As Byte
    Dim aByteHex() As String * 2
    
      If CryptGetHashParam( hHash, %HP_HASHSIZE, hashSize, 4, 0 ) = %FALSE Then
        WinErr = GetLastError : SysErrMsg( "CryptGetHashParam", 10 )
        Exit Function
      End If
      ReDim aByte( hashSize - 1 ) As Byte
      If CryptGetHashParam( hHash, %HP_HASHVAL, aByte( 0 ), hashSize, 0 ) = %FALSE Then
        WinErr = GetLastError : SysErrMsg( "CryptGetHashParam", 20 )
        Exit Function
      End If
      hashText = ""
      If Flag = %Bin Then
        hashText = Peek$( VarPtr( aByte( 0 ) ), hashSize )
      Else
        hashText = Space$( 2 * hashSize )
        ReDim aByteHex( hashSize - 1 ) As String * 2 At StrPtr( hashText )
        For i = 0 To hashSize - 1
          aByteHex( i ) = Hex$( aByte( i ), 2 )
        Next
      End If
      
      Function = %TRUE
    
    End Function
    
    ' *********************************************************************************************************************
    
    Function SystemErrorMessage( ByVal WinErr As Long, API As String, sFunction As String, ByVal Number As Long ) As String
      Local pBuffer As Asciiz Ptr, ncbBuffer As Dword
      Local sText As String
    
      ncbBuffer = FormatMessage( %FORMAT_MESSAGE_ALLOCATE_BUFFER Or %FORMAT_MESSAGE_FROM_SYSTEM Or _
      %FORMAT_MESSAGE_IGNORE_INSERTS, ByVal %Null, WinErr, %Null, ByVal VarPtr( pBuffer ), 0, ByVal %Null )
      If ncbBuffer Then
        sText = Peek$( pBuffer, ncbBuffer )
        sText = Remove$( sText, Any Chr$( 13 ) + Chr$( 10 ) )
        Function = $CrLf + "Error" + Format$( WinErr, " #####" ) + " during " + API + $CrLf + $CrLf + _
          Chr$(34) + sText + Chr$(34) + $CrLf + $CrLf + "Location : " + sFunction + IIf$( Number, Format$( Number, " ###" ), "" )
        LocalFree pBuffer
      Else
        Function = "Unknown error - Code:" + Format$( WinErr, " #####" )
      End If
    
    End Function
    
    ' ***************************************************************************************************************************
    
    ' Returns %True if a string is hexadecimal (A-F,a-f,0-9)
    Function Hex(s As String) As Long
    #Register None
    Local sPtr, sLen As Dword
    
      sPtr = StrPtr(s) - 1
      sLen = StrPtr(s) + Len(s)
      ! mov eax, sPtr ;Ptr To String - 1
      ! mov ecx, sLen ;Addr: End Of String
      StartLoop:
      ! inc eax
      ! cmp eax, ecx
      ! je  EndLoop
      ! mov bl, [eax]
      ! cmp bl, 65
      ! jl  NotAlpha
      ! cmp bl, 102
      ! jg  NotHex
      ! cmp bl, 71
      ! jl  Alpha
      ! cmp bl, 97
      ! jl NotAlpha
      Alpha:
      ! jmp StartLoop
      NotAlpha:
      ! cmp bl, 48
      ! jl NotHex
      ! cmp bl, 57
      ! jg NotHex
      ! jmp StartLoop
    NotHex:
      Exit Function
    EndLoop:
      Function = %True
    End Function
    
    ' ****************************************************
    Last edited by David Roberts; 12 Nov 2009, 08:06 AM. Reason: WinErr position numbers were out of sync

    Leave a comment:


  • David Roberts
    replied
    I've had some further thoughts on a single 'master key' as opposed to two passwords; one for the encryption and one for the HMAC

    The code that I was not happy with in the Code Project article is the following:
    Code:
    DeriveKey(const byte* key, unsigned ksize, const byte* label,
              unsigned lsize, HCRYPTKEY hKey)
    {
        if(!CryptCreateHash( hProvider, CALG_SHA1, 0, 0, &hHash)) {
            // Handle error
        }
     
        if (!CryptHashData( hHash, key, ksize, 0) ) {
            // Handle error
        }
     
        if (!CryptHashData( hHash, label, lsize, 0) ) {
            // Handle error
        }
     
        if (!CryptDeriveKey( hProvider, CALG_AES_128, hHash, dwFlags, &hKey)) {
            // Handle error
        }
    }
    key is the master key and label is either "encryption" or "authentication" for AES or HMAC respectively.

    The weakness here is similar to a weakness of using a fixed IV as opposed to a random IV; if indeed there is a weakness in AES of a fixed IV. There was certainly was one for DES.

    We are, of course, salting but salting normally uses a random salt. A random salt injects some entropy - dictionary words do not do that.

    The author says "The method we use to employ uniqueness among keys is similar to RFC 2898's KDF1". I had not visited there yet but did so. I had been there before and directed to it by Eddy Van Esch when I was working on PassKey.

    The idaea is to take a password and add a 64 bit random salt. This gives us access to 2^64 possible new passwords. We should not use the same password for AES and HMAC. There is a 50% chance that two random salts will collide in 2^32 trials; that is about 4000 million to one against. The RFC hashes 'P || S' where P is the original password and S is the salt with || denoting concatenation. It then hashes the resultant hash and repeats 1000 times. The final value is the 'derived key'. In our case the final value is fed into CryptDeriveKey.

    Its use in PassKey could be controlled to provide a workload such that we had to wait for a small time before getting a result. For us this small time was a one off. For an attacker this small time would be multiplied by the number of trials to break a key. With millions upon millions of trials an attack would be rendered infeasible.

    We can accommodate a little extra work but not too much. RFC 2898 is 10 years old and Moore's law has seen to it that our machines are much more powerful so I considered a 128 bit salt but stayed with the count of 1000 repeats.

    On my machine the additional work cost just over 5 milliseconds per encryption for the AES128/SHA256 combo and just over 10 milliseconds for the AES256/SHA512 combo. With decryption the salts are read rather than computed and the additional work was negligible. This is a small price to pay for the convenience of only having to supply one password.

    Previously, we used a HMackey of "0", "1" or "Whatever" for a bare bones encryption, a hash only or authentication respectively. Since there was no real difference in timing between a hash only and authentication then it is pointless to have a hash only as we are no longer required to supply a password for the authentication. A new parameter has been added, namely EncDecMode as Long. If %False then a bare bones encryption will be carried out. If %True then an authentication will be carried out.

    So, we now have sMasterKey instead of sAESKey and sHmacKey. The same test is applied as previously, namely if wholly hexadecimal and even numbered then sMasterKey will be treated as a binary key else ASCII. To get the full potential of a binary key we should use a bit length equal to the underlying hash algorithm. So, with AES128/SHA256 we should use a 256 bit sMasterKey.

    I am mindful of changing the decrypted file header rendering previous versions incompatible so the following code is called MSEncDec3. I am putting this project to bed so there will be no further changes.

    Attached is AuthDec2.dll. Normally a '2' would infer a major update but the '2' here indicates that two encryption modes are available ie a bare bones or authentication. It was felt that AuthDec.dll was a tad draconian in being authenticate only. A friend said that whilst authentication was a worthwhile inclusion he did not think that it was important for 'local' work ie files which would not leave his PC. I took the point but he doesn't know yet that only one password is now required whether we authenticate or not. However, the choice is available. I am so sold on the idea that I would authenticate all encryptions in future whether they leave my PC or not. It seems to me that whatever reason we have for encryption then if authentication is available we should use it.

    Here is an example using AuthDec2.dll
    Code:
    #Compile Exe
    #Dim All
     
    %NOMMIDS     = 1
    %NOGDI       = 1
    
    #Include "win32api.inc"
     
    %AES_128 = 26126
    %AES_192 = 26127
    %AES_256 = 26128
     
    Declare Function AuthDec2 Lib "AuthDec2.DLL" Alias "AuthDec2" ( _
      ByVal sInFile As String, ByVal WhichAlgorithm As Long, _
      ByVal sMasterKey As String, Opt EncDecMode As Long, _
      sInitialValues As String ) As String 
    
    Function PBMain
    Local  sFile, sMasterKey, sStatus, IV As String
    
      sFile = "Whatever" ' <----------------  [COLOR="Red"]Your filepath here[/COLOR]
      sMasterKey = "2E664237276E5BC4FA7DA0FCBE749F921700BF615333886A6ADAFB20F697B543" ' 256 bit
      ' Wholly hexadecimal and even numbered so will be treated as a binary string
    
      ' If either of following are uncommented then use [2] otherwise [1]
      'IV ="29B755EB595E19AD72B7C4275CA227D6"
      'IV = ""
      
      ' No fourth parameter so authenticate encryption
      sStatus = AuthDec2( sFile, %AES_128, sMasterKey ) ' [1]
      'sStatus = AuthDec2( sFile, %AES_128, sMasterKey, %True, IV ) ' [2]
          
      If sStatus = "OK" Then
        MessageBox 0, "File encrypted", "AuthDec2.dll", %MB_ICONINFORMATION + %MB_TOPMOST
       Else
        MessageBox 0, "Error encrypting file", "AuthDec2.dll", %MB_ICONERROR + %MB_TOPMOST
        GoTo terminate
      End If
      
      sFile = sFile + ".enc"
      sStatus = AuthDec2( sFile, %AES_128, sMasterKey ) ' [1]
      'sStatus = AuthDec2( sFile, %AES_128, sMasterKey, %True, IV ) ' [2]
      
      If sStatus = "OK" Then
        MessageBox 0, "File decrypted", "AuthDec2.dll", %MB_ICONINFORMATION + %MB_TOPMOST
      Else
        MessageBox 0, "Error decrypting file", "AuthDec2.ll", %MB_ICONERROR + %MB_TOPMOST
      End If
    
    terminate:  
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        Waitkey$
      #EndIf
      
    End Function
    Attached Files
    Last edited by David Roberts; 12 Nov 2009, 02:52 AM. Reason: EncDecMode is now optional

    Leave a comment:


  • David Roberts
    replied
    Code:
    #Compile Exe "MSEncDec2"
    #Dim All
    #Register None
    #Tools Off
    
    %NOMMIDS = 1
    %NOGDI = 1
    
    #Include "win32api.inc"
    #Include "wincrypt.inc"
    
    %BufferSize = 262144 ' 256KB
    %Bin = 1
    %Hex = 0
    
    Macro SysErrMsg( API, Number ) = MessageBox 0, SystemErrorMessage( WinErr, API, FuncName$, Number ), "MSEncDec2", %MB_ICONERROR + %MB_TOPMOST
    
    Type HMAC_Info
      HashAlgid As Word
      dummy As String * 6
    End Type
    
    Function PBMain
    Local sFile, sAESKey, sHmacKey, IV As String
    Local sStatus As String
    
      sFile = "Whatever" ' <---------------- [COLOR="Red"]Your filepath here[/COLOR]
         
      sAESKey = "200331AE31E63B4FE44069FF98B8F326"
      ' Wholly hexadecimal and even numbered so will be treated as a binary string
      
      sHmackey = "2E664237276E5BC4FA7DA0FCBE749F921700BF615333886A6ADAFB20F697B543" ' Authenticated encryption
      'sHmacKey = "0" ' Bare bones encryption/decryption
      'sHmacKey = "1" ' Data integrity check
      
      ' If either of following are uncommented then use [2] otherwise [1]
      'IV ="29B755EB595E19AD72B7C4275CA227D6"
      'IV = ""
      
      sStatus = MSEncDec2( sFile, %CALG_AES_128, sAESKey, sHmacKey ) ' [1]
      'sStatus = MSEncDec2( sFile, %CALG_AES_128, sAESKey, sHmacKey, IV ) ' [2]
        
      If sStatus = "OK" Then
        MessageBox 0, "File encrypted", "MSEncDec2", %MB_ICONINFORMATION + %MB_TOPMOST
       Else
        MessageBox 0, "Error encrypting file", "MSEncDec2", %MB_ICONERROR + %MB_TOPMOST
        GoTo terminate
      End If
      
      sFile = sFile + ".enc"
      sStatus = MSEncDec2( sFile, %CALG_AES_128, sAESKey, sHmacKey )
      If sStatus = "OK" Then
        MessageBox 0, "File decrypted", "MSEncDec2", %MB_ICONINFORMATION + %MB_TOPMOST
      Else
        MessageBox 0, "Error decrypting file", "MSEncDec2", %MB_ICONERROR + %MB_TOPMOST
      End If
    
    Terminate:  
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        Waitkey$
      #EndIf
      
    End Function
    
    '*******************************************************************************
    
    Function crGetDefaultRSAHandle As Long
    Local hProv, WinErr             As Long
    
    If CryptAcquireContext( hProv, ByVal %Null, ByVal %Null, %PROV_RSA_AES, 0 ) = %False Then
      If CryptAcquireContext( hProv, ByVal %Null, $MS_ENH_RSA_AES_PROV, %PROV_RSA_AES, %CRYPT_NEWKEYSET ) = %False Then
         WinErr = GetLastError : SysErrMsg( "CryptAcquireContext", 10 )
         Function = %False
      Else
         Function = hProv
      End If
    Else
      Function = hProv
    End If
    End Function
    
    '***************************************************************************************************
    
    Function MSEncDec2( ByVal sInFile As String, ByVal WhichAlgorithm As Long, ByVal sAESKey As String, _
                       ByVal sHmacKey As String, Opt sInitialValues As String ) As String
                       
    Local WinErr, hIn, hOut, i, NoOfBytes, HashLen As Long
    Local Dummy, sOutFile, pbdata, HashValue, PlainTextHash As String
    Local WhichHash, hProv, hHash, hEncDecKey, hIntegrityHash, hHMACKey As Long, BlockSize, CipherMode As Dword
    Local pdwDatalen As Dword
    Local DeriveKeydwFlags, IsInputEncrypted, FileLen As Dword, FinalSection, SectionCount, dwBuflen, Final As Long
    Dim IV() As Byte
    Dim HMACInfo As HMAC_Info
    'Local hHMACKey As Long
    
      Function = "OK" ' Look on the bright side <smile>
      
      ' Set up input out files
      If Dir$( sInFile ) = "" Then
        MessageBox 0, "Unable to find " + sInfile, "MSEncDec2", %MB_ICONWARNING + %MB_TOPMOST
         Function = ""
        GoTo TidyUp
      End If
    
      hIn = FreeFile
      Open sInFile For Binary Lock Shared As hIn
      If Err Then
        MessageBox 0, "Error" + Str$(Err) + " opening " + sInFile, "MSEncDec2", %MB_ICONERROR + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      FileLen = Lof (hIn)
      If Filelen = 0 Then
        MessageBox 0, "Input file" + $CrLf + $CrLf + sInFile + $CrLf + $CrLf + "is a zero byte file", "MSEncDec2", %MB_ICONWARNING + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      
      If LCase$( Right$( sInFile, 4 ) ) <> ".enc" Then
        sOutFile = sInFile & ".enc"
        ' eg e:\folder\myapp.exe ==> e:\folder\myapp.exe.enc
        IsInputEncrypted = %False
      Else
        sOutFile = Left$( sInFile, Len( sInFile ) - 4 )
        ' eg e:\folder\myapp.exe.enc ==> e:\folder\myapp.exe
        IsInputEncrypted = %True
      End If
      If Dir$( sOutFile ) <> "" Then
        Kill sOutFile
      End If
      
      hOut = FreeFile
      Open sOutFile For Binary As hOut
      If Err Then
        MessageBox 0, "Error" + Str$(Err) + " opening " + sOutFile, "MSEncDec2", %MB_ICONERROR + %MB_TOPMOST
        Function = ""
        GoTo TidyUp
      End If
      
      ' Are we going to use sAESKey as ascii or binary?
      If ( sAESKey <> "" ) And ( Len( sAESKey ) Mod 2 = 0 ) And  IsTrue Hex( sAESKey ) Then
        ' Treat as a binary string
        NoOfBytes = Len( sAESKey )/2
        For i = 0 To NoOfBytes - 1
          Dummy = Dummy + Chr$( Val( "&H" + Mid$( sAESKey, 2*i+1, 2 ) ) )
        Next
        sAESKey = Dummy
        Reset Dummy
      End If
      
      ' Are we going to use sHmackey as ascii or binary?
      If sHmacKey <> "0" And sHmackey <> "1" Then
        If ( sHmacKey <> "" ) And ( Len( sHmacKey ) Mod 2 = 0 ) And  IsTrue Hex( sHmacKey ) Then
          ' Treat as a binary string
          NoOfBytes = Len( sHmacKey )/2
          For i = 0 To NoOfBytes - 1
            Dummy = Dummy + Chr$( Val( "&H" + Mid$( sHmacKey, 2*i+1, 2 ) ) )
          Next
          sHmacKey = Dummy
          Reset Dummy
        End If
      End If
      
      ' Select hash algoritim according to chosen encryption algorithm
      ' You could add others, but why? Don't use stream ciphers - BlockSize = 0 is no longer checked for.
      Select Case WhichAlgorithm
        Case %CALG_AES_128
          WhichHash = %CALG_SHA_256
          HashLen = 32
          DeriveKeydwFlags = Mak(Dword, 0, 128)
        Case %CALG_AES_192
          WhichHash = %CALG_SHA_384
          HashLen = 48
          DeriveKeydwFlags = Mak(Dword, 0, 192)
        Case %CALG_AES_256
          WhichHash = %CALG_SHA_512
          HashLen = 64
          DeriveKeydwFlags = Mak(Dword, 0, 256)
        Case Else
          MessageBox 0, "I do not recognise the given encryption algorithm", "MSEncDec2", %MB_ICONWARNING + %MB_TOPMOST
          Function = ""
          GoTo TidyUp
      End Select
      
      hProv = crGetDefaultRSAHandle
      If hProv = 0 Then ' We have already been advised of a failure
        Function = ""
        Exit Function
      End If
      
      ' Get encrytion/decrytion key as hEncDecKey
      If CryptCreateHash( hProv, WhichHash, 0, 0, hHash ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 10 )
        Function = ""
        GoTo TidyUp
      Else
        If CryptHashData( hHash, ByVal StrPtr(sAESKey), Len( sAESKey ), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptHasData", 20 )
          Function = ""
          GoTo TidyUp
        Else
          If CryptDeriveKey( hProv, WhichAlgorithm, hHash, DeriveKeydwFlags, hEncDecKey ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 30 )
            Function = ""
            GoTo TidyUp
          End If
        End If
      End If
    
      ' Get the encryption algorithms block size - 16 for AES but we may not be using AES, why?
      If CryptGetKeyParam ( hEncDecKey, %KP_BLOCKLEN, BlockSize, SizeOf(BlockSize), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptGetKeyParam", 40 )
        Function = ""
        GoTo TidyUp
      End If
      
      BlockSize = BlockSize\8 ' In bytes
      
      ReDim IV(0 To BlockSize - 1 ) As Byte
      
      If IsFalse IsInputEncrypted Then
        
        If IsFalse VarPtr(sInitialValues) Then ' Optional IV not given so generate a random binary array IV and dump
        
          If CryptGenRandom( hProv, BlockSize, IV( 0 ) )= 0 Then
            WinErr = GetLastError : SysErrMsg( "CryptGenRandom", 60 )
            Function = ""
            GoTo TidyUp
          End If
          
        Else ' Optional IV given
        
          If ( sInitialValues <> "" ) Then
            If ( Len( sInitialValues ) Mod 2 = 0 ) And IsTrue Hex( sInitialValues ) Then
              If Len( sInitialValues ) < 2 * BlockSize Then
                sInitialValues = sInitialValues + Nul$( 2 * BlockSize - Len( sInitialValues ) )
              End If
              For i = 0 To BlockSize - 1
                IV(i) = Val( "&H" + Mid$( sInitialValues, 2*i+1, 2 ) )
              Next
            Else
              MessageBox 0, "Given IV is either not all hexadecimal or not even numbered", "MSEncDec2", %MB_ICONWARNING + %MB_TOPMOST
              Function = ""
              GoTo TidyUp
            End If
          End If
          
        End If ' IsFalse VarPtr(sInitialValues)
        
        If sHmacKey <> "0" Then ' Allocate header space for hash value
          Put$ hOut, Nul$(HashLen)
        End If
        
        ' If Optional IV given but was empty then we will fall through to here
        ' IV() will be empty, as created, effectively giving MS Default
        
        Put hOut, ,IV() ' dump IV before encrypting
          
      Else ' we are decrypting so get PlainText hash value if applicable then get IV
        
        If sHmacKey <> "0" Then ' get PlainText hash value
          Get$ hIn, HashLen, PlainTextHash
        End If
        
        Get hIn, ,IV()
        
      End If ' IsFalse IsInputEncrypted
      
      ' OK, we have our IV so set the Initial Values to ours
      If CryptSetKeyParam( hEncDecKey, %KP_IV, IV( 0 ), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptSetKeyParam", 70 )
        Function = ""
        GoTo TidyUp
      End If
       
      dwBuflen = %BufferSize ' Initial buffer size
      pdwDatalen = dwBuflen ' prior to the Final section
      FinalSection = FileLen\dwBuflen
      If (FileLen Mod dwBuflen) > 0 Then Incr FinalSection ' Data of 3.5, say, buffers worth will have 4 sections
      
      If sHmacKey <> "0" Then ' Data integrity check at least
      
        ' Get hIntegrityHash for 2nd parameter of CryptEncrypt/CryptDecrypt
        
        If CryptCreateHash( hProv, WhichHash, 0, 0, hIntegrityHash ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 80 )
          Function = ""
          GoTo TidyUp
        End If
        ' The above is all that we require for hashing
      
        If sHmacKey <> "1" Then ' Authentication
          
          ' Get HMAC key as hHMACKey
          If CryptHashData( hIntegrityHash, ByVal StrPtr( sHmacKey ), Len( sHmacKey ), 0 ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptHasData", 90 )
            Function = ""
            GoTo TidyUp
          Else
            If CryptDeriveKey( hProv, WhichAlgorithm, hIntegrityHash, 0, hHMACKey ) = %False Then
              WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 100 )
              Function = ""
              GoTo TidyUp
            End If
          End If
          
          If CryptCreateHash( hProv, %CALG_HMAC, hHMACKey, 0, hIntegrityHash ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 110 )
            Function = ""
            GoTo TidyUp
          End If
          
          HMACInfo.HashAlgid = WhichHash
          
          If CryptSetHashParam( hIntegrityHash, %HP_HMAC_INFO, HMACInfo, 0 ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptSetHashParam", 120 )
            Function = ""
            GoTo TidyUp
          End If          
          
        End If ' If sHmacKey <> "1"
       
      End If 'sHmacKey <> "0"
      
      Do
        Incr SectionCount
        Get$ hIn, dwBuflen, pbdata
        If SectionCount = FinalSection Then ' note the Final data & prepare for encryption padding
          pdwDatalen = Len(pbdata)
          If IsFalse IsInputEncrypted Then
            pbdata = pbdata + Space$(BlockSize - pdwDatalen Mod BlockSize) ' Provide for padding
            dwBuflen = Len(pbdata)
            ' Note that dwBuflen may be reduced from its initial size but it is not neccessary
            ' An increase is necessary if this pdwDatalen = initial buffer size  
          End If
          Final = %True
        End If
        ' hIntegrityHash will be zero if sHmackey = "0"
        If IsInputEncrypted Then
          If CryptDecrypt( hEncDecKey, hIntegrityHash, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptDecrypt", 130 )
            Function = ""
            GoTo TidyUp
          End If
        Else
          If CryptEncrypt( hEncDecKey, hIntegrityHash, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen, dwBuflen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptEncrypt", 140 )
            Function = ""
            GoTo TidyUp 
          End If
        End If 
        If Final Then
          Put$ hOut, Left$( pbdata, pdwDatalen)
        Else
          Put$ hOut, pbdata
        End If
      Loop Until Final
      
      If sHmackey <> "0" Then
        If GetHash( HashValue, hIntegrityHash, %Bin ) = %False Then GoTo TidyUp
        If IsFalse IsInputEncrypted Then
          Put hOut, 1, Hashvalue ' Dump the hash value to head of file
        Else
          If HashValue <> PlainTextHash Then ' Compare hash value with headers value
            Function = ""
            GoTo TidyUp
          End If
        End If
      End If
      
    TidyUp:  
      
      If hProv Then CryptReleaseContext hProv, 0
      If hHash Then CryptDestroyHash hHash
      If hEncDecKey Then CryptDestroyKey hEncDecKey
      If hIntegrityHash Then CryptDestroyHash hIntegrityHash
      If hHMACKey Then CryptDestroyKey hHMACKey
      Close hIn
      Close hOut
    
    End Function
    
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Function GetHash( hashText As String, hHash As Long, ByVal Flag As Long ) As Long
    ' If input Flag = 1 (%Bin) then hashText output is Binary
    ' If input Flag = 0 (%Hex) then hashText output is Hex
    
    Local i, hashSize, WinErr As Long
    
    Dim aByte() As Byte
    Dim aByteHex() As String * 2
    
      If CryptGetHashParam( hHash, %HP_HASHSIZE, hashSize, 4, 0 ) = %FALSE Then
        WinErr = GetLastError : SysErrMsg( "CryptGetHashParam", 10 )
        Exit Function
      End If
      ReDim aByte( hashSize - 1 ) As Byte
      If CryptGetHashParam( hHash, %HP_HASHVAL, aByte( 0 ), hashSize, 0 ) = %FALSE Then
        WinErr = GetLastError : SysErrMsg( "CryptGetHashParam", 20 )
        Exit Function
      End If
      hashText = ""
      If Flag = %Bin Then
        hashText = Peek$( VarPtr( aByte( 0 ) ), hashSize )
      Else
        hashText = Space$( 2 * hashSize )
        ReDim aByteHex( hashSize - 1 ) As String * 2 At StrPtr( hashText )
        For i = 0 To hashSize - 1
          aByteHex( i ) = Hex$( aByte( i ), 2 )
        Next
      End If
      
      Function = %TRUE
    
    End Function
    
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Function SystemErrorMessage( ByVal WinErr As Long, API As String, sFunction As String, ByVal Number As Long ) As String
      Local pBuffer As Asciiz Ptr, ncbBuffer As Dword
      Local sText As String
    
      ncbBuffer = FormatMessage( %FORMAT_MESSAGE_ALLOCATE_BUFFER Or %FORMAT_MESSAGE_FROM_SYSTEM Or %FORMAT_MESSAGE_IGNORE_INSERTS, _
        ByVal %Null, WinErr, %Null, ByVal VarPtr( pBuffer ), 0, ByVal %Null )
      If ncbBuffer Then
        sText = Peek$( pBuffer, ncbBuffer )
        sText = Remove$( sText, Any Chr$( 13 ) + Chr$( 10 ) )
        Function = $CrLf + "Error" + Format$( WinErr, " #####" ) + " during " + API + $CrLf + $CrLf + _
          Chr$(34) + sText + Chr$(34) + $CrLf + $CrLf + "Location : " + sFunction + IIf$( Number, Format$( Number, " ###" ), "" )
        LocalFree pBuffer
      Else
        Function = "Unknown error - Code:" + Format$( WinErr, " #####" )
      End If
    
    End Function
    
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ' Returns %True if a string is hexadecimal (A-F,a-f,0-9)
    Function Hex(s As String) As Long
    #Register None
    Local sPtr, sLen As Dword
    
      sPtr = StrPtr(s) - 1
      sLen = StrPtr(s) + Len(s)
      ! mov eax, sPtr ;Ptr To String - 1
      ! mov ecx, sLen ;Addr: End Of String
      StartLoop:
      ! inc eax
      ! cmp eax, ecx
      ! je  EndLoop
      ! mov bl, [eax]
      ! cmp bl, 65
      ! jl  NotAlpha
      ! cmp bl, 102
      ! jg  NotHex
      ! cmp bl, 71
      ! jl  Alpha
      ! cmp bl, 97
      ! jl NotAlpha
      Alpha:
      ! jmp StartLoop
      NotAlpha:
      ! cmp bl, 48
      ! jl NotHex
      ! cmp bl, 57
      ! jg NotHex
      ! jmp StartLoop
    NotHex:
      Exit Function
    EndLoop:
      Function = %True
    End Function
    Last edited by David Roberts; 7 Nov 2009, 11:47 AM.

    Leave a comment:


  • David Roberts
    replied
    Authenticated encryption.

    Added: To avoid wasting your time.
    The following applies to >= XP SP3. The code won't crash otherwise but it will fall over gracefully.

    A brief word first on encryption. Encryption is about privacy, confidentiality whatever you want to call it. It is not about data integrity. I am sure that most of you know that but there are some folk who think that an encrypted file is 'as safe as houses'. If an enrypted message was tampered with, the decryption process would be oblivious to it. In effect we have y = f(x). With decryption, x is the ciphertext, f is the decryption process and y is the resulting plaintext. F is simply a conduit between the x and y dimensions; that is its only purpose.

    In the main source code above the second parameter of CryptEncrypt and CryptDecrypt is zero. If it was a handle to a hash object then the incoming data would be hashed as well as encrypted/decrypted and done "simultaneously"; says the SDK. Not sure what Microsoft mean by "simultaneously", asynchronous or synchronous - threading or not. Anyway, in operation it is faster than wholly separate operations.

    My lead on this topic was got from A C++ AES/HMAC Class
    at 'The Code Project' plus scouring Google Search Code.

    A conclusion at the Code Project article, after a WinDbg session, was that messages (files in our context) are encrypted then authenticated as opposed to authenticated then encrypted. In other words, we hash the ciphertext and plaintext respectively.

    Authentication is done via HMAC so the hash object mentioned above is a HMAC hash object. It occurred to me whilst reading that if we used a simple hash, ie a 'H' if you like, then, although we would not get authentication, we would get a check on data integrity. That would be easier to code to start with so I did just that.

    Whilst playing around I found that ECB and CBC were producing the same hash value. My heart sank. What on earth have I done? A hex dump of the encrypted files showed me that they were very different besides the CBC being larger with a IV prepend.

    The same hash would result if the protocol was authenticate and then encrypt; contrary to Code Projects conclusion. I loaded HashFile and hashed the source file - the hash value was the same as that produced by the ECB and CBC runs. When I got the HMAC code working ECB and CBC also gave the same values but I could not use HashFile here as the MS HMAC is proprietary; as are Sun and Oracle, it seems.

    The article mentioned that authenticate/encrypt was insecure in some situations but encrypt/authenticate was secure in all situations. The article referred to an earlier article which cited research on this protocol. One conclusion of that research was
    The two provably safe Authenticate then Encrypt constructions are:

    * Block ciphers operated in CBC mode
    * Stream ciphers which XORs data with a pseudo-random pad
    Block ciphers are more efficient than Stream ciphers for disk encryption so I had already decided that only Block ciphers would be considered. AES is a block cipher and our default cipher mode is CBC.

    Sigh of relief. At some time I must find out why my system is different to the above articles test machine. The protocol is within the API and cannot be controlled from without else the services of WinDbg would not have been used.

    ECB was already hanging by a thread as far as I was concerned and this latest finding has killed it off for me and it is not an option in the following code which I've called MSEncDec2 so as not to overwrite MsEncDec.

    So, Block ciphers and CBC it is. Most of the block ciphers in the CryptoAPI are either old hat or are better suited to other adventures. DES, for example, at 56 bit is over 30 years old and was given its marching orders by NIST some time ago. Triple DES, at 168 bit, remains on NIST's good books but with my tests I found it to be much slower than AES 256. For encryption/decryption of files then I see no reason to use anything other than AES.

    The code in the next post uses three forms of encryption: A bare bones as in the above code, a data integrity check only, via a hash, and authenticated encryption via HMAC.

    With regard to what combination of encryption/hashing functions to use I favour matching security classification with security classification. To keep things simple I go with AES128/SHA256, AES192/SHA384 and AES256/SHA512.

    The article above thinks that it is asking too much in requiring a password for encryption and a password for hashing and proposes a master key to which we 'hash' in the word 'encryption' and 'authentication' respectively. I totally disagree. It is like going to a metal worker and asking for a chain to be made which is both light and strong. The metal worker says "I'll use titanium - it won't be cheap but it will be both light and strong" to which we respond with "Good, but could you make one of the links in cardboard?" The following code expects two passwords if data integrity or data integrity plus authentication is required. The same argument holds against using, for example, AES256/MD5 - MD5 would be a cardboard link. However, my approach can be easily changed if you disagree with me.

    When all of the sections have been encrypted the hash value calculated is added to the output file. The method employed by the following code, to make life easy, calculates a null string long enough to accept the hash value and Puts it to disk, followed by the IV and then the encrypted data and finally the calculated hash value is then Put to the head of the file. On decryption the hash value is read from the head of the file and noted. The IV is then read and then the following data decrypted. The calculated hash value is then compared with the one noted earlier. If all is well they will match.

    Now, problem. With an encrypt/authenticate protocol the hash value is of the encrypted data. They should not be transmitted at the same time by being joined at the hip in a single file. This is similar to sending a file with an accompanying hash value. If the pair are intercepted, the file could be changed and a new hash calculated and the new pair allowed to continue the journey. If sent under separate cover then if the hash was intercepted a 1st preimage attack would follow. On the other hand if the file was intercepted then a 2nd preimage attack would follow. If both are intercepted then we are in trouble. There is a saving grace though - the hash used in the article is HMAC. I have contacted a fellow forum member who knows more about crypto stuff than I do for his thoughts. It may be that a HMAC of the encrypted file and the encrypted file travelling together is OK.

    With our protocol, authenticate/encrypt, the hash value is of the plaintext and it can be transmitted at the same time as the encrypted data; whether it is a simple hash or a HMAC.

    Anyway, the difference in timings between a hash and authentication was negligible. This was expected. The MAC part of HMAC works on the given password and its influence cascades throughout the message 'crunching'. It is analogous to initial values. It has an absolute value so its relative percentage diminishes as the file size increases. It is a small percentage even for smallish files.

    So, the question as to whether to hash or authenticate encryption is the same as whether to hash or use HMAC on messages.

    For a 100MB file I'm getting 5.1 seconds for a bare bones AES256/SHA512. For both data integrity and data integrity plus authentication I'm getting 5.8 seconds. The file cache was cleared before each run. The time to read/write the data was 3 seconds. So, data integrity is costing less than 1 second for a 100MB file using the most expensive combination. Separate processes would see a hash at 3.5 seconds giving 8.6 seconds for a AES + Hash - about 50% more work than an integrated approach.

    If you have a need to encrypt a file do you have an argument against authenticated encryption? I don't.

    In keeping with the above attached is a dll. I have called it AuthDec.dll. Why? Well, a bare bones encryption and a data integrity only check is not available - it is an authenticated decryption routine - and nothing less. Have a lie down first - you'll need two passwords.

    An easy way to generate a cryptographic pseudo-random binary string password is to load HashFile, click on the down pointing chevron, choose MD5 ( for 128 bit ) or SHA256 ( for 256 bit ) and click on the 'R' button on the right. For 512 bit you'll need to add the switch /HMACWP to access Whirlpool; normally disabled in HMAC mode because I couldn't get any HMAC-Whirlpool test vectors. For 192 and 384 bit you can clip 256 and 512. AES passkeys can be got from PassKey as well.

    NB You will need to add the follwing API to WinCrypt.inc.
    Code:
    Declare Function CryptSetHashParam Lib "advapi32.dll" Alias "CryptSetHashParam" _
          ( ByVal hHash As Long, ByVal dwParam As Dword, pbdata As Any, _
            ByVal dwFlags As Long ) As Long
    Here is an example of using the library:
    Code:
    #Compile Exe
    #Dim All
     
    %NOMMIDS     = 1
    %NOGDI       = 1
    
    #Include "win32api.inc"
     
    %AES_128 = 26126
    %AES_192 = 26127
    %AES_256 = 26128
     
    Declare Function AuthDec Lib "AuthDec.DLL" Alias "AuthDec" ( _
      ByVal sInFile As String, ByVal WhichAlgorithm As Long, _
      ByVal sAESKey As String, ByVal sHmacKey As String, _
      Opt sInitialValues As String ) As String 
    
    Function PBMain
    Local  sFile, sAESKey, sHmacKey, sStatus, IV As String
    
      sFile = "Whatever" ' <----------------  Your filepath here
      sAESKey = "200331AE31E63B4FE44069FF98B8F326" ' 128 bit"
      sHMackey = "2E664237276E5BC4FA7DA0FCBE749F921700BF615333886A6ADAFB20F697B543" ' 256 bit
      
      ' If either of following are uncommented then use [2] otherwise [1]
      'IV ="29B755EB595E19AD72B7C4275CA227D6"
      'IV = ""
      
      sStatus = AuthDec( sFile, %AES_128, sAESKey, sHmacKey ) ' [1]
      'sStatus = AuthDec( sFile, %AES_128, sAESKey, sHmacKey, IV ) ' [2]
          
      If sStatus = "OK" Then
        MessageBox 0, "File encrypted", "AuthDec.dll", %MB_ICONINFORMATION + %MB_TOPMOST
       Else
        MessageBox 0, "Error encrypting file", "AuthDec.dll", %MB_ICONERROR + %MB_TOPMOST
        GoTo terminate
      End If
      
      sFile = sFile + ".enc"
      sStatus = AuthDec( sFile, %AES_128, sAESKey, sHmacKey )
      If sStatus = "OK" Then
        MessageBox 0, "File decrypted", "AuthDec.dll", %MB_ICONINFORMATION + %MB_TOPMOST
      Else
        MessageBox 0, "Error decrypting file", "AuthDec.ll", %MB_ICONERROR + %MB_TOPMOST
      End If
    
    terminate:  
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        Waitkey$
      #EndIf
      
    End Function
    Have fun.
    Attached Files
    Last edited by David Roberts; 7 Nov 2009, 12:23 PM. Reason: Added a library usage example.

    Leave a comment:


  • David Roberts
    replied
    I have just uploaded a revised MSEncDec.dll according to the last post. CBC users would never see the bug.

    I am now working on Authenticated Encryption in line with an article in CodeProject last March. Instead of hashing and encryption as two separate steps they are done simultaneously. From a timing point of view the results are falling between a straight encryption and two separate steps. I am also looking at an option to forgo authentication but keep a data integrity check. Early tests show that it is costing me just over one second to know that a deciphered file is passing a SHA256 Hash compared with the original plaintext file on a 100MB file.

    Leave a comment:


  • David Roberts
    replied
    Don't use ECB mode without looking at the above source code again. I got my If/End If nesting out of sync at the setting of a user IV. It attempted to set a IV with ECB. At least it shows that the API error trapping is working.

    Leave a comment:


  • David Roberts
    replied
    Hi Peter. We cannot chat here - Source code forum.

    I have opened a discussion thread in the Windows forum.

    Leave a comment:


  • Peter Redei
    replied
    David,

    I am getting this error:



    Any idea?


    Thanks,


    Peter Redei
    Last edited by Peter Redei; 4 Nov 2009, 03:24 PM.

    Leave a comment:


  • David Roberts
    replied
    I use PBWin much more often than PBCC and my PBCC etiquette is somewhat challenged.

    If the library is used in a console application the messages from it are Printed and therefore will get mixed up with the console applications output.

    Example from Function MSEncDec in post #1.

    Code:
    If Err Then
      #If %Def(%Pb_Cc32)
        Print "Error"; Str$(Err); " opening "; sOutFile: Print
      #Else
        MsgBox "Error" + Str$(Err) + " opening " + sOutFile, %MB_ICONEXCLAMATION, "MSEncDec.exe"
      #EndIf
      Function = ""
      GoTo TidyUp
    End If
    The print statement doesn't even say who the message is from.

    MessageBox works for both PBWin and PBCC so the above may be replaced with:

    Code:
    If Err Then
      MessageBox 0, "Error" + Str$(Err) + " opening " + sOutFile, "MSEncDec.dll", %MB_ICONERROR + %MB_TOPMOST
      Function = ""
      GoTo TidyUp
    End If
    So, we now get the same message regardless of which compiler we are using and it is clear who is talking to us.

    The library has been updated to reflect this approach and uploaded above.

    If you intend using the source code above in a PBCC application you may want to edit it along the above lines.

    Leave a comment:


  • David Roberts
    replied
    Nothing else better to do.

    dll uploaded.

    Syntax

    Input:

    sInfile: Filepath of target file. For decryption the extension must be enc.

    WhichAlgorithm: %CALG_AES_128/192/256 and others previously available.

    WhichMode: If 0 then default CBC will be used else ECB.

    sPassword: If wholly hexadecimal and even number of characters then sPassword will be treated as a binary string else ASCII.

    sInitialValues: If option not taken then IV will be cryptographic pseudo random bytes. If option taken then user supplied IV. For AES this will normally be 32 hex characters for a 128 bit block size. Having said that, if an empty string is supplied then the MS default IV will be used ie all zeros. If more than required is supplied then excess will be ignored. If less than required then the shortfall will be filled with zeros. sInitialValues is not required for decryption as IV is in the header of the decrypted file. If the ECB cipher mode has been chosen then any IV is ignored as ECB does not use any feedback.

    Output: Empty string if failure or "OK" if success.

    Example usage.
    Code:
    #Compile Exe
    #Dim All
     
    %NOMMIDS     = 1
    %NOGDI       = 1
     
    %AES_128 = 26126
    %AES_192 = 26127
    %AES_256 = 26128
     
    Declare Function MSEncDec Lib "MSEncDec.DLL" Alias "MSEncDec" ( ByVal sInFile As String, _
                                                                    ByVal WhichAlgorithm As Long, _
                                                                    ByVal WhichMode As Long, _
                                                                    ByVal sPassword As String, _
                                                                    Opt sInitialValues As String ) As String 
    
    Function PBMain
    Local sStatus, sFile, sPassword As String
    
      sFile = "Whatever" ' [COLOR="Red"]Your filepath here[/COLOR]
    	
      sPassword = "Compile without Compromise!"
    	
      sStatus = MSEncDec( sFile, %AES_128, 0, sPassword )
      ' No IV given so generate random IV - preferred modus operandi
        
      If sStatus = "OK" Then
        #If %Def(%Pb_Cc32)
          Print "File encrypted"
        #Else
          MsgBox "File Encrypted"
        #EndIf
      Else
        #If %Def(%Pb_Cc32)
          Print "Error encrypting file"
        #Else
          MsgBox "Error encrypting file"
        #EndIf
        GoTo terminate
      End If
    	
      sFile = sFile + ".enc"
      sStatus = MSEncDec( sFile, %AES_128, 0, sPassword )
      If sStatus = "OK" Then
        #If %Def(%Pb_Cc32)
          Print: Print "File decrypted"
        #Else
          MsgBox "File Decrypted"
        #EndIf
      Else
        #If %Def(%Pb_Cc32)
          Print "Error decrypting file"
        #Else
          MsgBox "Error decrypting file"
        #EndIf
      End If
    
    terminate:	
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        Waitkey$
      #EndIf
    	
    End Function
    Attached Files
    Last edited by David Roberts; 5 Nov 2009, 05:54 AM.

    Leave a comment:


  • David Roberts
    replied
    I have just tidied up the code - it was a messy formatting job. I've never been able to post directly from an editor to the forum without formatting going haywire.

    I have corrected a few things which I need to do when posting.

    For example I don't use wincrypt.inc. I have mentioned, once a long time ago, that I have yet to use richedit so I moved the richedit.inc to a safe place, copied wincrypt.inc to \PBWin90\WinAPI\ and renamed it to richedit.inc. When I use Inclean I get a lean wincrypt.inc by selecting richedit. Needless to say I forget to change richedit to wincrypt when I post.

    I have just dropped a wincrypt.inc into \PBWin\WinAPI\ which is identical to the 'new' richedit. That way I can use wincrypt.inc and post without making a mistake. For a lean compile I just need to change wincrypt.inc to richedit.inc.

    Are you still with me?

    I always forget to remove the filepath I last used.

    Anyway, by simply adding your file to encrypt the above will compile without further ado as it should have done when first posted.

    Forgive these idiosyncrasies, I'm not as young as I was.

    Leave a comment:


  • David Roberts
    replied
    '############################ REPLY ############################
    'Don Dickinson, Member
    'posted November 09, 1999 10:36 PM


    '
    ' wincrypt.inc
    '
    ' Partial translation of Microsoft's CryptoAPI to Power Basic 32-bit
    ' Translated from Visual C++ header files by Don Dickinson
    ' Nov, 1999
    '

    ' Updated to include high order SHAs and AES - see sections marked ' Added

    Declare Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" _
    ( hCryptProv As Long, zContainer As Asciiz, zProvider As Asciiz, _
    ByVal dwProvType As Dword, ByVal dwFlags As Dword ) As Long

    Declare Function CryptGetProvParam Lib "advapi32.dll" Alias "CryptGetProvParam" _
    ( ByVal hProv As Long, ByVal dwParam As Dword, pbData As Any, _
    dwDataLen As Dword, ByVal dwFlags As Dword ) As Long

    Declare Function CryptReleaseContext Lib "advapi32.dll" Alias "CryptReleaseContext" _
    ( ByVal hCryptProv As Long, ByVal dwFlags As Dword ) As Long

    Declare Function CryptSetProvider Lib "advapi32.dll" Alias "CryptSetProviderA" _
    ( zProvName As Asciiz, ByVal dwProvType As Dword ) As Long

    Declare Function CryptSetProvParam Lib "advapi32.dll" Alias "CryptSetProvParam" _
    ( ByVal hCryptProv As Long, ByVal dwParam As Dword, pbData As Any, ByVal dwFlags As Dword ) As Long

    Declare Function CryptCreateHash Lib "advapi32.dll" Alias "CryptCreateHash" _
    ( ByVal hProv As Long, ByVal iAlgID As Long, ByVal hKey As Long, _
    ByVal dwFlags As Dword, hHash As Long ) As Long

    Declare Function CryptHashData Lib "advapi32.dll" Alias "CryptHashData" _
    ( ByVal hHash As Long, pbData As Any, ByVal dwDatalen As Dword, _
    ByVal dwFlags As Long ) As Long

    ' Added [1]
    ' ~~~~~~~~~

    Declare Function CryptGetKeyParam Lib "advapi32.dll" Alias "CryptGetKeyParam" _
    ( ByVal hKey As Long, ByVal dwParam As Dword, pbdata As Any, _
    pdwDatalen As Long, ByVal dwFlags As Dword ) As Long

    Declare Function CryptSetKeyParam Lib "advapi32.dll" Alias "CryptSetKeyParam" _
    ( ByVal hKey As Long, ByVal dwParam As Dword, pbdata As Any, _
    ByVal dwFlags As Dword ) As Long

    Declare Function CryptGetHashParam Lib "advapi32.dll" Alias "CryptGetHashParam" _
    ( ByVal hHash As Long, ByVal dwParam As Dword, pbData As Any, _
    pdwDataLen As Long, ByVal dwFlags As Long ) As Long

    Declare Function CryptDuplicateHash Lib "advapi32.dll" Alias "CryptDuplicateHash" _
    ( ByVal hHash As Long, ByVal pdwReserved As Dword, ByVal dwFlags As Dword, phHash As Dword) As Long
    ' ~~~~~~~~~

    Declare Function CryptDeriveKey Lib "advapi32.dll" Alias "CryptDeriveKey" _
    ( ByVal hProv As Long, ByVal AlgID As Long, ByVal hBaseData As Long, _
    ByVal dwFlags As Long, hKey As Long ) As Long

    Declare Function CryptEncrypt Lib "advapi32.dll" Alias "CryptEncrypt" _
    ( ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, _
    ByVal dwFlags As Dword, pbData As Any, pdwDataLen As Long, _
    ByVal dwBufLen As Long ) As Long

    Declare Function CryptDecrypt Lib "advapi32.dll" Alias "CryptDecrypt" _
    ( ByVal hKey As Long, ByVal hHash As Long, ByVal Final As Long, _
    ByVal dwFlags As Dword, pbData As Any, pdwDataLen As Long ) As Long

    Declare Function CryptDestroyHash Lib "advapi32.dll" Alias "CryptDestroyHash" _
    ( ByVal hHash As Long ) As Long

    Declare Function CryptDestroyKey Lib "advapi32.dll" Alias "CryptDestroyKey" _
    ( ByVal hKey As Long ) As Long

    ' Added [1]
    ' ~~~~~~~~~
    Declare Function CryptGenRandom Lib "advapi32.dll" Alias "CryptGenRandom" _
    ( ByVal hProv As Long, ByVal dwLen As Dword, pbBuffer As Byte) As Long
    ' ~~~~~~~~~

    $MS_DEF_PROV_A = "Microsoft Base Cryptographic Provider v1.0"
    $MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"

    ' Added [1]
    ' ~~~~~~~~~
    $MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
    ' ~~~~~~~~~

    '- Return constants for CryptAquireContext
    ' %ERROR_INVALID_PARAMETER = defined in win32api.inc
    ' %ERROR_NOT_ENOUGH_MEMORY = defined in win32api.inc

    #If %Def(%THESE_AREN_NOT_YET_DEFINED)
    %NTE_BAD_FLAGS
    %NTE_BAD_KEYSET
    %NTE_BAD_KEYSET_PARAM
    %NTE_BAD_PROV_TYPE
    %NTE_BAD_SIGNATURE
    %NTE_EXISTS
    %NTE_KEYSET_ENTRY_BAD
    %NTE_KEYSET_NOT_DEF
    %NTE_NO_MEMORY
    %NTE_PROV_DLL_NOT_FOUND
    %NTE_PROV_TYPE_ENTRY_BAD
    %NTE_PROV_TYPE_NO_MATCH
    %NTE_PROV_TYPE_NOT_DEF
    %NTE_PROVIDER_DLL_FAIL
    %NTE_SIGNATURE_FILE_BAD
    #EndIf

    %PROV_RSA_FULL = 1
    %PROV_RSA_SIG = 2
    %PROV_DSS = 3
    %PROV_FORTEZZA = 4
    %PROV_MS_EXCHANGE = 5
    %PROV_SSL = 6
    %PROV_RSA_SCHANNEL = 12
    %PROV_DSS_DH = 13
    %PROV_EC_ECDSA_SIG = 14
    %PROV_EC_ECNRA_SIG = 15
    %PROV_EC_ECDSA_FULL = 16
    %PROV_EC_ECNRA_FULL = 17
    %PROV_SPYRUS_LYNKS = 20
    ' Added [1]
    ' ~~~~~~~~~
    %PROV_RSA_AES = 24
    ' ~~~~~~~~~
    ' dwFlags definitions For CryptAcquireContext
    %CRYPT_VERIFYCONTEXT = &hF0000000
    %CRYPT_NEWKEYSET = &h00000008
    %CRYPT_DELETEKEYSET = &h00000010
    %CRYPT_MACHINE_KEYSET = &h00000020

    ' CryptSetProvParam
    '
    %PP_CLIENT_HWND = 1
    %PP_CONTEXT_INFO = 11
    %PP_KEYEXCHANGE_KEYSIZE = 12
    %PP_SIGNATURE_KEYSIZE = 13
    %PP_KEYEXCHANGE_ALG = 14
    %PP_SIGNATURE_ALG = 15
    %PP_DELETEKEY = 24

    '- CryptGetProvParam
    '
    %PP_ENUMALGS = 1
    %PP_ENUMCONTAINERS = 2
    %PP_IMPTYPE = 3
    %PP_NAME = 4
    %PP_VERSION = 5
    %PP_CONTAINER = 6
    %PP_CHANGE_PASSWORD = 7
    %PP_KEYSET_SEC_DESCR = 8
    %PP_CERTCHAIN = 9
    %PP_KEY_TYPE_SUBTYPE = 10
    %PP_PROVTYPE = 16
    %PP_KEYSTORAGE = 17
    %PP_APPLI_CERT = 18
    %PP_SYM_KEYSIZE = 19
    %PP_SESSION_KEYSIZE = 20
    %PP_UI_PROMPT = 21
    %PP_ENUMALGS_EX = 22

    ' dwFlag definitions For CryptGenKey
    %CRYPT_EXPORTABLE = &h00000001
    %CRYPT_USER_PROTECTED = &h00000002
    %CRYPT_CREATE_SALT = &h00000004
    %CRYPT_UPDATE_KEY = &h00000008
    %CRYPT_NO_SALT = &h00000010
    %CRYPT_PREGEN = &h00000040
    %CRYPT_RECIPIENT = &h00000010
    %CRYPT_INITIATOR = &h00000040
    %CRYPT_ONLINE = &h00000080
    %CRYPT_SF = &h00000100
    %CRYPT_CREATE_IV = &h00000200
    %CRYPT_KEK = &h00000400
    %CRYPT_DATA_KEY = &h00000800

    ' dwFlags definitions For CryptDeriveKey
    %CRYPT_SERVER = &h00000400

    %KEY_LENGTH_MASK = &hFFFF0000

    ' dwFlag definitions For CryptExportKey
    %CRYPT_Y_ONLY = &h00000001
    %CRYPT_SSL2_SLUMMING = &h00000002

    ' dwFlags definitions For CryptHashSessionKey
    %CRYPT_LITTLE_ENDIAN = &h00000001

    ' dwFlag definitions For CryptSetProviderEx And CryptGetDefaultProvider
    %CRYPT_MACHINE_DEFAULT = &h00000001
    %CRYPT_USER_DEFAULT = &h00000002
    %CRYPT_DELETE_DEFAULT = &h00000004

    ' exported key blob definitions
    %SIMPLEBLOB = &h1
    %PUBLICKEYBLOB = &h6
    %PRIVATEKEYBLOB = &h7
    %PLAINTEXTKEYBLOB = &h8

    %AT_KEYEXCHANGE = 1
    %AT_SIGNATURE = 2

    %CRYPT_USERDATA = 1

    ' dwParam
    %KP_IV = 1 ' Initialization vector
    %KP_SALT = 2 ' Salt value
    %KP_PADDING = 3 ' Padding values
    %KP_MODE = 4 ' Mode of the cipher
    %KP_MODE_BITS = 5 ' Number of bits To feedback
    %KP_PERMISSIONS = 6 ' Key permissions Dword
    %KP_ALGID = 7 ' Key algorithm
    %KP_BLOCKLEN = 8 ' Block Size of the cipher
    %KP_KEYLEN = 9 ' Length of key In bits
    %KP_SALT_EX = 10 ' Length of salt In bytes
    %KP_P = 11 ' DSS/Diffie-Hellman P value
    %KP_G = 12 ' DSS/Diffie-Hellman G value
    %KP_Q = 13 ' DSS Q value
    %KP_X = 14 ' Diffie-Hellman X value
    %KP_Y = 15 ' Y value
    %KP_RA = 16 ' Fortezza RA value
    %KP_RB = 17 ' Fortezza RB value
    %KP_INFO = 18 ' For putting information into an RSA envelope
    %KP_EFFECTIVE_KEYLEN = 19 ' setting And getting RC2 effective key length
    %KP_SCHANNEL_ALG = 20 ' For setting the Secure Channel algorithms
    %KP_CLIENT_RANDOM = 21 ' For setting the Secure Channel Client Random Data
    %KP_SERVER_RANDOM = 22 ' For setting the Secure Channel Server Random Data
    %KP_RP = 23
    %KP_PRECOMP_MD5 = 24
    %KP_PRECOMP_SHA = 25
    %KP_CERTIFICATE = 26 ' For setting Secure Channel certificate Data (PCT1)
    %KP_CLEAR_KEY = 27 ' For setting Secure Channel clear key Data (PCT1)
    %KP_PUB_EX_LEN = 28
    %KP_PUB_EX_VAL = 29

    ' KP_PADDING
    %PKCS5_PADDING = 1 ' PKCS 5 (sec 6.2) padding method
    %RANDOM_PADDING = 2
    %ZERO_PADDING = 3

    ' KP_MODE
    %CRYPT_MODE_CBC = 1 ' Cipher block chaining
    %CRYPT_MODE_ECB = 2 ' Electronic code book
    %CRYPT_MODE_OFB = 3 ' Output feedback mode
    %CRYPT_MODE_CFB = 4 ' Cipher feedback mode
    %CRYPT_MODE_CTS = 5 ' Ciphertext stealing mode

    ' KP_PERMISSIONS
    %CRYPT_ENCRYPT = &h0001 ' Allow encryption
    %CRYPT_DECRYPT = &h0002 ' Allow decryption
    %CRYPT_EXPORT = &h0004 ' Allow key To be exported
    %CRYPT_READ = &h0008 ' Allow parameters To be Read
    %CRYPT_WRITE = &h0010 ' Allow parameters To be Set
    %CRYPT_MAC = &h0020 ' Allow MACs To be used With key
    %CRYPT_EXPORT_KEY = &h0040 ' Allow key To be used For exporting keys
    %CRYPT_IMPORT_KEY = &h0080 ' Allow key To be used For importing keys

    %HP_ALGID = &h0001 ' Hash algorithm
    %HP_HASHVAL = &h0002 ' Hash value
    %HP_HASHSIZE = &h0004 ' Hash value Size
    %HP_HMAC_INFO = &h0005 ' information For creating an HMAC

    '
    ' CryptGetProvParam
    '
    %PP_ENUMALGS = 1
    %PP_ENUMCONTAINERS = 2
    %PP_IMPTYPE = 3
    %PP_NAME = 4
    %PP_VERSION = 5
    %PP_CONTAINER = 6
    %PP_CHANGE_PASSWORD = 7
    %PP_KEYSET_SEC_DESCR = 8 ' Get/Set security descriptor of keyset
    %PP_CERTCHAIN = 9 ' For retrieving certificates From tokens
    %PP_KEY_TYPE_SUBTYPE = 10
    %PP_PROVTYPE = 16
    %PP_KEYSTORAGE = 17
    %PP_APPLI_CERT = 18
    %PP_SYM_KEYSIZE = 19
    %PP_SESSION_KEYSIZE = 20
    %PP_UI_PROMPT = 21
    %PP_ENUMALGS_EX = 22

    %CRYPT_FIRST = 1
    %CRYPT_NEXT = 2

    %CRYPT_IMPL_HARDWARE = 1
    %CRYPT_IMPL_SOFTWARE = 2
    %CRYPT_IMPL_MIXED = 3
    %CRYPT_IMPL_UNKNOWN = 4

    ' key storage flags
    %CRYPT_SEC_DESCR = &h00000001
    %CRYPT_PSTORE = &h00000002
    %CRYPT_UI_PROMPT = &h00000004

    ' protocol flags
    %CRYPT_FLAG_PCT1 = &h0001
    %CRYPT_FLAG_SSL2 = &h0002
    %CRYPT_FLAG_SSL3 = &h0004
    %CRYPT_FLAG_TLS1 = &h0008


    '
    ' STT defined Providers
    '
    %PROV_STT_MER = 7
    %PROV_STT_ACQ = 8
    %PROV_STT_BRND = 9
    %PROV_STT_ROOT = 10
    %PROV_STT_ISS = 11

    '
    ' Algorithm IDs And Flags
    '
    ' Algorithm classes
    %ALG_CLASS_ANY = 0
    %ALG_CLASS_SIGNATURE = 8192 '(1 << 13) '8192
    %ALG_CLASS_MSG_ENCRYPT = 16384 '(2 << 13) '16384
    %ALG_CLASS_DATA_ENCRYPT = 24576 '(3 << 13) '24576
    %ALG_CLASS_HASH = 32768 '(4 << 13) '32768
    %ALG_CLASS_KEY_EXCHANGE = 40960 '(5 << 13) '40960

    ' Algorithm types
    %ALG_TYPE_ANY = 0
    %ALG_TYPE_DSS = 512 '(1 << 9) '512
    %ALG_TYPE_RSA = 1024 '(2 << 9) '1024
    %ALG_TYPE_BLOCK = 1536 '(3 << 9) '1536
    %ALG_TYPE_STREAM = 2048 '(4 << 9) '2048
    %ALG_TYPE_DH = 2560 '(5 << 9) '2560
    %ALG_TYPE_SECURECHANNEL = 3072 '(6 << 9) '3072


    ' Generic Sub-ids
    %ALG_SID_ANY = 0

    ' Some RSA Sub-ids
    %ALG_SID_RSA_ANY = 0
    %ALG_SID_RSA_PKCS = 1
    %ALG_SID_RSA_MSATWORK = 2
    %ALG_SID_RSA_ENTRUST = 3
    %ALG_SID_RSA_PGP = 4

    ' Some DSS Sub-ids
    '
    %ALG_SID_DSS_ANY = 0
    %ALG_SID_DSS_PKCS = 1
    %ALG_SID_DSS_DMS = 2

    ' Block cipher Sub ids
    ' DES sub_ids
    %ALG_SID_DES = 1
    %ALG_SID_3DES = 3
    %ALG_SID_DESX = 4
    %ALG_SID_IDEA = 5
    %ALG_SID_CAST = 6
    %ALG_SID_SAFERSK64 = 7
    %ALG_SID_SAFERSK128 = 8
    %ALG_SID_3DES_112 = 9
    %ALG_SID_CYLINK_MEK = 12
    %ALG_SID_RC5 = 13
    ' Added [1]
    ' ~~~~~~~~~
    %ALG_SID_AES_128 = 14
    %ALG_SID_AES_192 = 15
    %ALG_SID_AES_256 = 16
    ' ~~~~~~~~~
    ' Fortezza Sub-ids
    %ALG_SID_SKIPJACK = 10
    %ALG_SID_TEK = 11

    ' KP_MODE
    %CRYPT_MODE_CBCI = 6 ' ANSI CBC Interleaved
    %CRYPT_MODE_CFBP = 7 ' ANSI CFB Pipelined
    %CRYPT_MODE_OFBP = 8 ' ANSI OFB Pipelined
    %CRYPT_MODE_CBCOFM = 9 ' ANSI CBC + OF Masking
    %CRYPT_MODE_CBCOFMI = 10 ' ANSI CBC + OFM Interleaved

    ' RC2 Sub-ids
    %ALG_SID_RC2 = 2

    ' Stream cipher Sub-ids
    %ALG_SID_RC4 = 1
    %ALG_SID_SEAL = 2

    ' Diffie-Hellman Sub-ids
    %ALG_SID_DH_SANDF = 1
    %ALG_SID_DH_EPHEM = 2
    %ALG_SID_AGREED_KEY_ANY = 3
    %ALG_SID_KEA = 4

    ' Hash Sub ids
    %ALG_SID_MD2 = 1
    %ALG_SID_MD4 = 2
    %ALG_SID_MD5 = 3
    %ALG_SID_SHA = 4
    %ALG_SID_SHA1 = 4
    %ALG_SID_MAC = 5
    %ALG_SID_RIPEMD = 6
    %ALG_SID_RIPEMD160 = 7
    %ALG_SID_SSL3SHAMD5 = 8
    %ALG_SID_HMAC = 9

    ' Added [1]
    ' ~~~~~~~~~
    %ALG_SID_SHA_256 = 12
    %ALG_SID_SHA_384 = 13
    %ALG_SID_SHA_512 = 14
    ' ~~~~~~~~~

    ' secure channel Sub ids
    %ALG_SID_SSL3_MASTER = 1
    %ALG_SID_SCHANNEL_MASTER_HASH = 2
    %ALG_SID_SCHANNEL_MAC_KEY = 3
    %ALG_SID_PCT1_MASTER = 4
    %ALG_SID_SSL2_MASTER = 5
    %ALG_SID_TLS1_MASTER = 6
    %ALG_SID_SCHANNEL_ENC_KEY = 7

    ' Our silly example Sub-id
    %ALG_SID_EXAMPLE = 80

    ' algorithm identifier definitions
    %CALG_MD2 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_MD2
    %CALG_MD4 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_MD4
    %CALG_MD5 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_MD5
    %CALG_SHA = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SHA
    %CALG_SHA1 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SHA1

    ' Added [1]
    ' ~~~~~~~~~
    %CALG_SHA_256 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SHA_256
    %CALG_SHA_384 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SHA_384
    %CALG_SHA_512 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SHA_512
    ' ~~~~~~~~~

    %CALG_MAC = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_MAC
    %CALG_RSA_SIGN = %ALG_CLASS_SIGNATURE Or %ALG_TYPE_RSA Or %ALG_SID_RSA_ANY
    %CALG_DSS_SIGN = %ALG_CLASS_SIGNATURE Or %ALG_TYPE_DSS Or %ALG_SID_DSS_ANY
    %CALG_RSA_KEYX = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_RSA Or %ALG_SID_RSA_ANY
    %CALG_DES = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_DES
    %CALG_3DES_112 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_3DES_112
    %CALG_3DES = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_3DES
    %CALG_RC2 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_RC2
    %CALG_RC4 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_STREAM Or %ALG_SID_RC4
    ' Added [1]
    ' ~~~~~~~~~
    %CALG_AES_128 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_AES_128
    %CALG_AES_192 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_AES_192
    %CALG_AES_256 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_AES_256
    ' ~~~~~~~~~
    %CALG_SEAL = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_STREAM Or %ALG_SID_SEAL
    %CALG_DH_SF = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_DH Or %ALG_SID_DH_SANDF
    %CALG_DH_EPHEM = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_DH Or %ALG_SID_DH_EPHEM
    %CALG_AGREEDKEY_ANY = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_DH Or %ALG_SID_AGREED_KEY_ANY
    %CALG_KEA_KEYX = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_DH Or %ALG_SID_KEA
    %CALG_HUGHES_MD5 = %ALG_CLASS_KEY_EXCHANGE Or %ALG_TYPE_ANY Or %ALG_SID_MD5
    %CALG_SKIPJACK = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_SKIPJACK
    %CALG_TEK = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_TEK
    %CALG_CYLINK_MEK = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_CYLINK_MEK
    %CALG_SSL3_SHAMD5 = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_SSL3SHAMD5
    %CALG_SSL3_MASTER = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_SSL3_MASTER
    %CALG_SCHANNEL_MASTER_HASH = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_SCHANNEL_MASTER_HASH
    %CALG_SCHANNEL_MAC_KEY = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_SCHANNEL_MAC_KEY
    %CALG_SCHANNEL_ENC_KEY = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_SCHANNEL_ENC_KEY
    %CALG_PCT1_MASTER = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_PCT1_MASTER
    %CALG_SSL2_MASTER = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_SSL2_MASTER
    %CALG_TLS1_MASTER = %ALG_CLASS_MSG_ENCRYPT Or %ALG_TYPE_SECURECHANNEL Or %ALG_SID_TLS1_MASTER
    %CALG_RC5 = %ALG_CLASS_DATA_ENCRYPT Or %ALG_TYPE_BLOCK Or %ALG_SID_RC5
    %CALG_HMAC = %ALG_CLASS_HASH Or %ALG_TYPE_ANY Or %ALG_SID_HMAC

    ' [1] David Roberts

    Leave a comment:


  • David Roberts
    replied
    Code:
    #Compile Exe
    #Dim All
    #Register None
    #Tools Off
    
    %NOMMIDS     = 1
    %NOGDI       = 1
    
    #Include "win32api.inc"
    #Include "wincrypt.inc"
    
    %BufferSize = 262144 ' 256KB
    
    Macro SysErrMsg( API, Number ) = MessageBox 0, SystemErrorMessage( WinErr, API, FuncName$, Number ), "AES", %MB_ICONERROR + %MB_TOPMOST
    
    '******************************************************************************
    
    Function PBMain
    Local sFile, sPassword, IV As String
    Local sStatus As String
    
      sFile = "Whatever"  ' [COLOR="Red"]What do you want to encrypt today?[/COLOR] <smile>
     
      sPassword = "BD1C42C334ADF3B59609C66A691D3CF394993D69CB3F988367EC3CB695ADA606"
      ' Wholly hexadecimal and even numbered so will be treated as a binary string
    
      ' If either of following are uncommented then use [2] otherwise [1]
      'IV ="29B755EB595E19AD72B7C4275CA227D6"
      'IV = ""
    
      ' If third parameter is non-zero then cipher mode ECB will be used. In this case IV is academic.
      sStatus = MSEncDec( sFile, %CALG_AES_256, 0, sPassword ) ' [1]
      'sStatus = MSEncDec( sFile, %CALG_AES_256, 0, sPassword, IV ) ' [2]
    
      If sStatus = "OK" Then
        #If %Def(%Pb_Cc32)
          Print "File encrypted"
        #Else
          MsgBox "File Encrypted"
        #EndIf
      Else
        #If %Def(%Pb_Cc32)
          Print "Error encrypting file"
        #Else
          MsgBox "Error encrypting file"
        #EndIf
        GoTo terminate
      End If
      
      sFile = sFile + ".enc"
      sStatus = MSEncDec( sFile, %CALG_AES_256, 0, sPassword )
      If sStatus = "OK" Then
        #If %Def(%Pb_Cc32)
          Print: Print "File decrypted"
        #Else
          MsgBox "File Decrypted"
        #EndIf
      Else
        #If %Def(%Pb_Cc32)
          Print "Error decrypting file"
        #Else
          MsgBox "Error decrypting file"
        #EndIf
      End If
    
    terminate:
      #If %Def(%Pb_Cc32)
        Print: Print "Press a key to exit"
        WAITKEY$
      #EndIf
    
    End Function
    
    '*******************************************************************************
    
    Function crGetDefaultRSAHandle As Long
    Local hProv, WinErr             As Long
    
      If CryptAcquireContext( hProv, ByVal %Null, ByVal %Null, %PROV_RSA_AES, 0 ) = %False Then
        If CryptAcquireContext( hProv, ByVal %Null, $MS_ENH_RSA_AES_PROV, %PROV_RSA_AES, %CRYPT_NEWKEYSET ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptAcquireContext", 10 )
          Function = %False
        Else
          Function = hProv
        End If
      Else
        Function = hProv
      End If
      
    End Function
    
    '***************************************************************************************************
    
    Function MSEncDec( ByVal sInFile As String, ByVal WhichAlgorithm As Long, ByVal WhichMode As Long, _
                       ByVal sPassword As String, Opt sInitialValues As String ) As String
    
    Local WinErr, hIn, hOut, i, NoOfBytes As Long
    Local Dummy, sOutFile, pbdata As String
    Local hProv, hHash, hKey As Long, BlockSize, CipherMode As Dword
    Local pdwDatalen As Dword
    Local IsInputEncrypted, FileLen As Dword, FinalSection, SectionCount, dwBuflen, Final As Long
    Dim IV() As Byte
    
      Function = "OK" ' Look on the bright side <smile>
    
      If ( sPassword <> "" ) And ( Len( sPassword ) Mod 2 = 0 ) And  IsTrue Hex( sPassword ) Then
        ' Treat as a binary string
        NoOfBytes = Len( sPassword )/2
        For i = 0 To NoOfBytes - 1
          Dummy = Dummy + Chr$( Val( "&H" + Mid$( sPassword, 2*i+1, 2 ) ) )
        Next
        sPassword = Dummy
        Reset Dummy
      End If
    
      hProv = crGetDefaultRSAHandle
      If hProv = 0 Then ' We have already been advised of a failure
        Function = ""
        Exit Function
      End If
    
      If CryptCreateHash( hProv, %CALG_SHA_256, 0, 0, hHash ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptCreateHash", 10 )
        Function = ""
        GoTo TidyUp
      Else
        If CryptHashData( hHash, ByVal StrPtr(sPassword), Len( sPassword ), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptHasData", 20 )
          Function = ""
          GoTo TidyUp
        Else
          If CryptDeriveKey( hProv, WhichAlgorithm, hHash, %CRYPT_EXPORTABLE, hKey ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptDeriveKey", 30 )
            Function = ""
            GoTo TidyUp
          End If
        End If
      End If
    
      If Dir$( sInFile ) = "" Then
        #If %Def(%Pb_Cc32)
          Print "Unable to find ";sInFile: Print
        #Else
          MsgBox "Unable to find " + sInFile, %MB_ICONEXCLAMATION, "MSEncDec.exe"
        #EndIf
        Function = ""
        GoTo TidyUp
      End If
    
      hIn = FreeFile
      Open sInFile For Binary Lock Shared As hIn
      If Err Then
        #If %Def(%Pb_Cc32)
          Print "Error"; Str$(Err);" opening "; sInfile: Print
        #Else
          MsgBox "Error" + Str$(Err) + " opening " + sInfile, %MB_ICONEXCLAMATION, "MSEncDec.exe"
        #EndIf
        Function = ""
        GoTo TidyUp
      End If
      FileLen = Lof (hIn)
      If Filelen = 0 Then
        #If %Def(%Pb_Cc32)
          Print sInFile; " is a zero byte file": Print
        #Else
          MsgBox "Input file" + $CrLf + $CrLf + sInFile + $CrLf + $CrLf + "is a zero byte file.", %MB_ICONEXCLAMATION, "MSEncDec.exe"
        #EndIf
        Function = ""
        GoTo TidyUp
      End If
    
      If LCase$( Right$( sInFile, 4 ) ) <> ".enc" Then
        sOutFile = sInFile & ".enc"
        ' eg e:\folder\myapp.exe ==> e:\folder\myapp.exe.enc
        IsInputEncrypted = %False
      Else
        sOutFile = Left$( sInFile, Len( sInFile ) - 4 )
        ' eg e:\folder\myapp.exe.enc ==> e:\folder\myapp.exe
        IsInputEncrypted = %True
      End If
      If Dir$( sOutFile ) <> "" Then
        Kill sOutFile
      End If
    
      hOut = FreeFile
      Open sOutFile For Binary As hOut
      If Err Then
        #If %Def(%Pb_Cc32)
          Print "Error"; Str$(Err); " opening "; sOutFile: Print
        #Else
          MsgBox "Error" + Str$(Err) + " opening " + sOutFile, %MB_ICONEXCLAMATION, "MSEncDec.exe"
        #EndIf
        Function = ""
        GoTo TidyUp
      End If
    
      ' Get the encryption algorithms block size - 16 for AES but we may not be using AES <smile>
      ' Indeed, we will get a block size of zero for a stream cipher <double smile>
      If CryptGetKeyParam ( hKey, %KP_BLOCKLEN, BlockSize, SizeOf(BlockSize), 0 ) = %False Then
        WinErr = GetLastError : SysErrMsg( "CryptGetKeyParam", 40 )
        Function = ""
        GoTo TidyUp
      End If
    
      BlockSize = BlockSize\8 ' In bytes
    
      ' If BlockSize = 0 then the IV are of no interest to us - we are not using a block cipher
      If BlockSize > 0 Then ' Check which cipher mode
    
        If IsTrue WhichMode Then ' Set the cipher mode to ECB - the default is CBC
      
          ' IV are of no interest to us here either
          CipherMode = %CRYPT_MODE_ECB
          If CryptSetKeyParam ( hKey, %KP_MODE, CipherMode, 0 ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptSetKeyParam", 50 )
            Function = ""
            GoTo TidyUp
          End If
      
        Else ' CBC mode in operation so Set or Get IV
      
          ReDim IV(0 To BlockSize - 1 ) As Byte
      
          If IsFalse IsInputEncrypted Then
      
            If IsFalse VarPtr(sInitialValues) Then ' Optional IV not given so generate a random binary array IV and dump
      
              If CryptGenRandom( hProv,  BlockSize, IV( 0 ) )= 0 Then
                WinErr = GetLastError : SysErrMsg( "CryptGenRandom", 60 )
                Function = ""
                GoTo TidyUp
              End If
      
            Else ' Optional IV given
      
              If ( sInitialValues <> "" ) Then
                If ( Len( sInitialValues ) Mod 2 = 0 ) And IsTrue Hex( sInitialValues ) Then
                  If Len( sInitialValues ) < 2 * BlockSize Then
                    sInitialValues = sInitialValues + Nul$( 2 * BlockSize - Len( sInitialValues ) )
                  End If
                  For i = 0 To BlockSize - 1
                    IV(i) = Val( "&H" + Mid$( sInitialValues, 2*i+1, 2 ) )
                  Next
                Else
                  #If %Def(%Pb_Cc32)
                    Print "Given IV is invalid: Either not all hexadecimal or not even numbered": Print
                  #Else
                    MsgBox "Given IV is invalid: Either not all hexadecimal or not even numbered", %MB_ICONEXCLAMATION, "MSEncDec.exe"
                  #EndIf
                  Function = ""
                  GoTo TidyUp
                End If
              End If
      
            End If
      
            ' If Optional IV given but was empty then we will fall through to here
            ' IV() will be empty, as created, effectively giving MS Default
      
            Put hOut, ,IV() ' dump IV before encrypting
      
          Else ' we are decrypting so get get IV
      
            Get hIn, ,IV()
      
          End If ' IsFalse IsInputEncrypted
      
        End If ' IsTrue WhichMode
        
        ' Put the following before the last statement
        [COLOR="Red"]' OK, we have our IV so set the Initial Values to ours
        If CryptSetKeyParam( hKey, %KP_IV, IV( 0 ), 0 ) = %False Then
          WinErr = GetLastError : SysErrMsg( "CryptSetKeyParam", 70 )
          Function = ""
          GoTo TidyUp
        End If[/COLOR]
    
      End If ' BlockSize > 0
    
      dwBuflen = %BufferSize ' Initial buffer size
      pdwDatalen = dwBuflen ' prior to the Final section
      FinalSection = FileLen\dwBuflen
      If (FileLen Mod dwBuflen) > 0 Then Incr FinalSection ' Data of 3.5, say, buffers worth will have 4 sections
    
      Do
        Incr SectionCount
        Get$ hIn, dwBuflen, pbdata
        If SectionCount = FinalSection Then ' note the Final data & prepare for encryption padding
          pdwDatalen = Len(pbdata)
          If IsFalse IsInputEncrypted Then
            pbdata = pbdata + Space$(BlockSize - pdwDatalen Mod BlockSize) ' Provide for padding
            dwBuflen = Len(pbdata)
            ' Note that dwBuflen may be reduced from its initial size but it is not neccessary
            ' An increase is necessary if this pdwDatalen = initial buffer size
          End If
          Final = %True
        End If
          If IsInputEncrypted Then
            If CryptDecrypt( hKey, 0, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptDecrypt", 80 )
            Function = ""
            GoTo TidyUp
          End If
        Else
          If CryptEncrypt( hKey, 0, Final, 0, ByVal StrPtr( pbdata ), pdwDatalen, dwBuflen ) = %False Then
            WinErr = GetLastError : SysErrMsg( "CryptEncrypt", 90 )
            Function = ""
            GoTo TidyUp
          End If
        End If
        If Final Then
          Put$ hOut, Left$( pbdata, pdwDatalen)
        Else
          Put$ hOut, pbdata
        End If
      Loop Until Final
    
    TidyUp:
    
      If hProv Then CryptReleaseContext hProv, 0
      If hHash Then CryptDestroyHash hHash
      If hKey Then CryptDestroyKey hKey
      Close hIn
      Close hOut
    
    End Function
    
    ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Function SystemErrorMessage( ByVal WinErr As Long, API As String, sFunction As String, ByVal Number As Long ) As String
      Local pBuffer As Asciiz Ptr, ncbBuffer As Dword
      Local sText As String
    
      ncbBuffer = FormatMessage( %FORMAT_MESSAGE_ALLOCATE_BUFFER Or %FORMAT_MESSAGE_FROM_SYSTEM Or %FORMAT_MESSAGE_IGNORE_INSERTS, _
        ByVal %Null, WinErr, %Null, ByVal VarPtr( pBuffer ), 0, ByVal %Null )
      If ncbBuffer Then
        sText = Peek$( pBuffer, ncbBuffer )
        sText = Remove$( sText, Any Chr$( 13 ) + Chr$( 10 ) )
        Function = $CrLf + "Error" + Format$( WinErr, " #####" ) + " during " + API + $CrLf + $CrLf + _
          Chr$(34) + sText + Chr$(34) + $CrLf + $CrLf + "Location : " + sFunction + IIf$( Number, Format$( Number, " ###" ), "" )
        LocalFree pBuffer
      Else
        Function = "Unknown error - Code:" + Format$( WinErr, " #####" )
      End If
    
    End Function
    
    '*******************************************************
    
    ' Returns %True if a string is hexadecimal (A-F,a-f,0-9)
    Function Hex(s As String) As Long
    #Register None
    Local sPtr, sLen As Dword
    
      sPtr = StrPtr(s) - 1
      sLen = StrPtr(s) + Len(s)
      ! mov eax, sPtr ;Ptr To String - 1
      ! mov ecx, sLen ;Addr: End Of String
      StartLoop:
      ! inc eax
      ! cmp eax, ecx
      ! je  EndLoop
      ! mov bl, [eax]
      ! cmp bl, 65
      ! jl  NotAlpha
      ! cmp bl, 102
      ! jg  NotHex
      ! cmp bl, 71
      ! jl  Alpha
      ! cmp bl, 97
      ! jl NotAlpha
      Alpha:
      ! jmp StartLoop
      NotAlpha:
      ! cmp bl, 48
      ! jl NotHex
      ! cmp bl, 57
      ! jg NotHex
      ! jmp StartLoop
    NotHex:
      Exit Function
    EndLoop:
      Function = %True
    End Function
    Last edited by David Roberts; 4 Nov 2009, 06:16 PM.

    Leave a comment:


  • David Roberts
    started a topic PBWin/PBCC AES 128/192/256 in XP SP3

    AES 128/192/256 in XP SP3

    This is a sibling thread to SHA256/384/512 in XP SP3.

    There we looked at how to access the higher ordered SHAs. AES is accessed in the same way - via the new Cryptographic Service Provider CSP).

    Quote from Eddy Van Esch
    The simplest method is named ECB (Electronic Code Book):
    You add a number of bytes ('padding') so that your plaintext length becomes a multiple of the block size (16 bytes for AES).
    You split up the plaintext in n blocks of each 16 bytes long. Every block is encrypted and concatenated to form the ciphertext.
    After decryption, the padded bytes are removed from the plaintext.
    So, the ciphertext must contain extra data to know how many bytes were padded.
    Every encrypted block is independent from any other encrypted block.

    There are several other methods, where the plaintext blocks are combined with other datablocks, usually the ciphertext of the previously encrypted plaintext block.
    Most know methods are:
    - CIPHER BLOCK CHAINING MODE (CBC)
    - CIPHER FEEDBACK MODE (CFB)
    - OUTPUT FEEDBACK MODE (OFB)
    - COUNTER MODE (CTR)

    These methods have the benefit over ECB mode that the ciphertext of a certain block is influenced by the previously encrypted blocks.

    The tricky part for the first 3 methods is that, for the first encryped block, the plaintext is combined with a so-called 'Initial Vector' or IV.
    The user can freely determine what this IV is. It does not even have to be kept a secret.
    Depending on what IV you choose, your entire ciphertext looks different.

    Which of the different block cipher methods (except ECB) is the 'best', is usually a matter of personal preference. I have never seen any real objective arguments why one should be better than the other. Most experts agree that CBC is very safe.

    So, even if 2 people use the same block cipher mode for AES (CBC, ... all except ECB) and the same password and the same plaintext, still the ciphertext will look different if they use a different IV... This is the 'if' bit for all cipher block modes except for ECB.
    Only two of the methods Eddy mentions are fully implemented in the MS CryptoAPI, namely ECB and CBC. CBC is the default but the following code allows ECB as an alternative. The weakness of ECB is that any repetitions in the plaintext will see repetitions in the ciphertext and that may be advantageous to an attacker. Whilst there will be many situations where ECB is fine, we don't have to concern ourselves when employing CBC as repetitions in the plaintext are not reflected in the ciphertext and hence CBCs popularity.

    With regard to IV the MS default is all zeros. They could have chosen all 255, or all 99 or whatever. They could have chosen random bytes. The old DES block cipher had a weakness in that if an attacker got hold of a sufficient number of ciphertexts using the same IV then a successful attack was possible. I should add that this weakness has not been found in AES but I must confess to an unease of using the same IV forever even though I have no justification for such unease. The following code allows us to supply our own IV, as a hex string, and we may change it as many times as we wish. The obvious criteria is that the same IV must be used for both encryption and decryption. If the supplied IV is all zeros the result will be the same as the MS default IV. This may be simplified by passing an empty string. In practice those of us uneasy about using the same IV forever would probably not change it as often as we would like. Most of us tend to be lazy about changing passwords.

    Another approach is to generate a random IV for each and every encryption. The likelihood of a IV ever repeating is 2^128 to 1 against with an AES block size of 16 bytes. This totally removes any unease of using the same IV all the time even though, as mentioned, I have no justification for such unease. The random IV is got by implementing the CryptGenRandom API, a cryptographic pseudo random number generator.

    So, what we do with the generated IV? The method employed here is to dump the IV to disk and follow on with the encryption. The IV then becomes a header for the output file. On decryption we simply read the header first and then decrypt the following data. The ciphertext will then be 16 bytes longer than the plaintext.

    Using a random IV is the default for the following code. A user IV is got by an optional string parameter in the main functions parameter list.

    Naturally, a password is required of us. This may be ascii. In some other situations we may have no option but to use an ascii password but if a binary string is allowed then, I for one, would advocate that. In the following code if a password is wholly hexadecimal and has an even number of characters then it will be treated as a binary string. We don't have a choice here. "ABCD", for example, will be treated as a binary string whether we like it or not.

    Even though our main purpose here is AES the ciphers used with the older CSP are still available. The block size then cannot be 'hard wired' and rather than have a look up table we can use the CryptGetKeyParam API to determine, amongst several other parameters, the block size for a given cipher. I have used the 56/112/168 bit variants of the DES block cipher, which uses a block size of 64 bit as opposed to 128 bit for AES. CryptGetKeyParam has been added to WinCrypt.inc along with the equates required for AES and found in post #3. This updates the WinCrypt.inc used in the opening sentences link. Setting the cipher mode, ECB or CBC, and a user IV is done via the CryptSetKeyParam API and this has been added to WinCrypt.inc as well. CryptGenRandom is also there.

    Rather than read a file in its entirity we read blocks of 256KB. A 100MB test file peaked at just over 4MB usage in Task Manager. I have looked at buffer sizes many times before and found that using large buffers does not improve the speed of operation for this type of application. By 'type of application' I mean one primarily involved in reading data and 'crunching' it. Hashing applications are of this type. So, you could, for example, encrypt a 700MB iso image without having to worry about RAM usage.

    The core code for encryption/decryption is done via the CryptEncrypt/CryptDecrypt APIs. The third parameter for both is called Final. If Final is true then this is a trigger to pad the block for encryption or remove the padding for decryption. I had looked a VirtualAlocc/CreateFile combo plus %FILE_FLAG_NO_BUFFERING for reading and writing but whilst reading was OK, writing was a pain as we are restricted to sector size. I am sure that we could work around this but at what cost so the PB Open was used to fill a string. The padding aspect is all done for us but we have to intervene in the event of a file to be encrypted being a multiple of the input buffer size. If we do not increase the buffer to accomodate the padding then although we have said Final is true the CryptEncrypt API will fail reckoning that more data is available.

    We save the day in the Do/Loop processing the input buffer with
    Code:
    Incr SectionCount
    Get$ hIn, dwBuflen, pbdata
    If SectionCount = FinalSection Then ' note the Final data & prepare for encryption padding
      pdwDatalen = Len(pbdata)
      If IsFalse IsInputEncrypted Then
        pbdata = pbdata + Space$(BlockSize - pdwDatalen Mod BlockSize) ' Provide for padding
        dwBuflen = Len(pbdata)
        ' Note that dwBuflen may be reduced from its initial size but it is not neccessary
        ' An increase is necessary if this pdwDatalen = initial buffer size
      End If
      Final = %True
    End If
    pdwDatalen tells the CryptEncrypt/CryptDecrypt APIs the amount of data to 'crunch'. Before the Final section this will be the whole input buffer. It will also be the whole input buffer if the Final section is equal to the input buffer. This is why we have to intervene. I should add that since the initial input buffer is an integral multiple of the block size then if it was fixed in size padding would not create an over run of the input buffer when the data read is less than the input buffer. However, this is academic as the 'saving' code adjusts the input buffer according to pdwDatalen whether or not an adjustment is needed. I didn't want to add extra code saying don't adjust the input buffer if the final section is less than the input buffer.

    The interesting statement is in the provision for padding. If the block size is zero then pbdata is not changed, and hence the input buffer, that is no padding is applied. This is a result of BlockSize being zero and 'p Mod q' evaluating to zero if either or both expressions are zero. So, although the code is for block ciphers it will work for stream ciphers such as RC4 since Final is ignored and, as we have seen, padding does not occur.

    So, the following code will work for all of the symmetric-key ciphers in the CryptoAPI.

    I have not done extensive tests on timing. What I have found is that on my system AES 128 is about 20% faster than AES 256. This is significant but not as much as I would have thought. With AES 128 half of the time was spent reading and writing data with the other half 'crunching' the data. So, even if the AES implementation could be halved the total time would fall by only 25% as the bottleneck would then be the hard drive. My system comprises an Intel E6700 and a SATA hard drive. It follows then that with a slower hard drive the less of that 20% will be seen compared with my system. A SSD will see more of that 20%. Only a drive with an infinite speed will see all of that 20%.

    The bottom line for me is that a 100MB file is read, crunched and written in about five seconds with AES 256 and, as mentioned earlier, RAM got hit for only 4MB. It would be pointless to give tables of timings as your bottom line will depend upon how fast your system can Read/Write and how fast can it 'crunch'.

    Here is a summary of the main functions input/output.

    Input:

    sInfile: Filepath of target file. For decryption the extension must be enc.

    WhichAlgorithm: %CALG_AES_128/192/256 and others previously available.

    WhichMode: If 0 then default CBC will be used else ECB.

    sPassword: If wholly hexadecimal and even number of characters then sPassword will be treated as a binary string else ASCII.

    sInitialValues: If option not taken then IV will be cryptographic pseudo random bytes. If option taken then user supplied IV. For AES this will normally be 32 hex characters for a 128 bit block size. Having said that, if an empty string is supplied then the MS default IV will be used ie all zeros. If more than required is supplied then excess will be ignored. If less than required then the shortfall will be filled with zeros. sInitialValues is not required for decryption as IV is in the header of the decrypted file. If the ECB cipher mode has been chosen then any IV is ignored as ECB does not use any feedback.

    Output: Empty string if failure or "OK" if success.

    With such functions output is usually false or true for failure or success respectively but my departure is gearing up for a follow up project.

    In a few days I will post a dll.

    As always, have fun.
    Last edited by David Roberts; 2 Nov 2009, 01:43 PM. Reason: Spelling
Working...
X