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

  • PBWin/PBCC 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

  • #2
    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.

    Comment


    • #3
      '############################ 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

      Comment


      • #4
        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.

        Comment


        • #5
          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.

          Comment


          • #6
            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.

            Comment


            • #7
              David,

              I am getting this error:



              Any idea?


              Thanks,


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

              Comment


              • #8
                Hi Peter. We cannot chat here - Source code forum.

                I have opened a discussion thread in the Windows forum.

                Comment


                • #9
                  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.

                  Comment


                  • #10
                    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.

                    Comment


                    • #11
                      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.

                      Comment


                      • #12
                        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.

                        Comment


                        • #13
                          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

                          Comment


                          • #14
                            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

                            Comment


                            • #15
                              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.

                              Comment


                              • #16
                                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.

                                Comment

                                Working...
                                X