Option Explicit
Option Base 0

' frmCryptoSys.frm
' A demonstration of some of the encryption functions in the CryptoSys(tm) API.

'********************* COPYRIGHT NOTICE*********************
' Copyright (c) 2001-12 DI Management Services Pty Limited.
' All rights reserved.
' This code is provided as a suggested interface to the CryptoSys API.
' Use at your own risk. Make your own tests and check that this code
' does what you expect. Please report any bugs to <www.di-mgt.com.au>
' The latest version of CryptoSys(tm) API and a licence
' may be obtained from <www.cryptosys.net>.
' This copyright notice must always be left intact.
'************** END OF COPYRIGHT NOTICE*********************

' NOTE: Because this tries to do everything, it's more
' complicated than you'd need in a normal implementation.

' CAUTION: This is just a test-bed demo. It is not meant to be
' represesentative of good security code practices.
' There are limited error handling facilities, too.

' Invalid-length keys and IVs entered by the user are just padded with zeroes
' to avoid failures - don't do this in practice!

' REVISION HISTORY:
' Jan 2011: Allowed Decrypt button to work without doing Encrypt first
' Sep 2007: Revised for v4.0
' Jul 2006: Substantial revision for v3.2
' Jan 2006: minor update for v3.1.
' Feb 2005: minor update.
' Sep 2001: First published.
' ****************

Private Sub cmdClose_Click()
    End
End Sub

Private Sub cmdEncrypt_Click()
' Encrypt the plain text as required using hex strings
#If Not cccDebug Then
    On Error GoTo HandleError
#End If
    Dim nBlkLen As Long
    Dim nRet As Long
    Dim sMode As String
    Dim strPlain As String
    Dim strCipher As String
    Dim strHexKey As String
    Dim strHexIV As String
    
    ' Get current values for key and IV
    strHexKey = SetKey()
    strHexIV = SetIV()
    
    ' Clear output
    Me.txtInputBlock = ""
    Me.txtCipher = ""
    Me.txtCipherHex = ""
    Me.txtCipher64 = ""
    
    ' Convert plain text into hex if necessary
    If Me.optPTAlpha Then
        strPlain = cnvHexStrFromString(Me.txtPlain)
    Else
        If Not IsValidHex(Me.txtPlain) Then
            MsgBox "Invalid hex in plain text"
            GoTo Done
        End If
        strPlain = Me.txtPlain
    End If
    
    ' Get block length for chosen cipher
    nBlkLen = GetBlockLen()
    
    ' Then pad input if required
    If NeedPadding() Then
        strPlain = padHexString(strPlain, nBlkLen)
    End If
    
    ' Show input data as hex
    Me.txtInputBlock = strPlain
    
    ' Set up output string to required length
    strCipher = String(Len(strPlain), " ")
    
    ' Get the current mode
    sMode = GetMode()
    
    ' Now encrypt as per algorithm
    If Me.optDES.Value Then
        nRet = DES_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    ElseIf Me.optTDEA.Value Then
        nRet = TDEA_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    ElseIf Me.optAES128 Then
        nRet = AES128_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    ElseIf Me.optAES192 Then
        nRet = AES192_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    ElseIf Me.optAES256 Then
        nRet = AES256_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    ElseIf Me.optBLF Then
        nRet = BLF_HexMode(strCipher, strPlain, strHexKey, ENCRYPT, sMode, strHexIV)
    End If
    
    If nRet <> 0 Then
        Call DisplayCryptoSysError(nRet)
        Exit Sub
    End If

    ' Display results in hex, "raw" and base64 format
    Me.txtCipherHex = strCipher
    Me.txtCipher = cnvStringFromHexStr(strCipher)
    Me.txtCipher64 = cnvB64StrFromBytes(cnvBytesFromHexStr(strCipher))
    Me.cmdDecrypt.Enabled = True
    
Done:
    Exit Sub
HandleError:
    Call HandleVBError
    Resume Done

End Sub

Private Sub cmdDecrypt_Click()
' Decrypt the cipher text using hex strings
#If Not cccDebug Then
    On Error GoTo HandleError
#End If
    Dim nBlockLen As Long
    Dim nRet As Long
    Dim sMode As String
    Dim strCipher As String
    Dim strOutputHex As String
    Dim strDecrypt As String
    Dim strHexKey As String
    Dim strHexIV As String
    
    nBlockLen = GetBlockLen()
    
    ' Get current values for key and IV
    strHexKey = SetKey()
    strHexIV = SetIV()
    
    ' Clear output
    Me.txtDecrypt = ""
    Me.txtDecryptHex = ""
    Me.txtOutputBlock = ""
    
    ' Get the current ciphertext in hex format
    strCipher = Me.txtCipherHex
    If Len(strCipher) = 0 Then
        MsgBox "ERROR: No ciphertext to decrypt!"
        GoTo Done
    End If

    strOutputHex = String(Len(strCipher), " ")
    
    ' Get the current mode
    sMode = GetMode()
   
    ' Now decrypt as per current algorithm
    If Me.optDES.Value Then
        nRet = DES_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    ElseIf Me.optTDEA.Value Then
        nRet = TDEA_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    ElseIf Me.optAES128 Then
        nRet = AES128_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    ElseIf Me.optAES192 Then
        nRet = AES192_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    ElseIf Me.optAES256 Then
        nRet = AES256_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    ElseIf Me.optBLF Then
        nRet = BLF_HexMode(strOutputHex, strCipher, strHexKey, DECRYPT, sMode, strHexIV)
    End If
    
    ' Check whether OK
    If nRet <> 0 Then
        Call DisplayCryptoSysError(nRet)
        Exit Sub
    End If
    
    ' Show the unpadded output block
    Me.txtOutputBlock = strOutputHex
    
    ' Strip padding if nec
    If NeedPadding() Then
        strDecrypt = unpadHexString(strOutputHex, nBlockLen)
        If Len(strDecrypt) = Len(strOutputHex) Then
            MsgBox "Decryption error - invalid padding bytes found"
            Exit Sub
        End If
    Else
        strDecrypt = strOutputHex
    End If
    
    ' Display output in HEX and ANSI format
    Me.txtDecryptHex = strDecrypt
    Me.txtDecrypt = cnvStringFromHexStr(strDecrypt)
    
Done:
    Exit Sub
HandleError:
    Call HandleVBError
    Resume Done
    
End Sub

Private Sub cmdGenIV_Click()
    Call GenerateIV
End Sub

Private Sub cmdGenKey_Click()
    ' Generate a random key silently
    Call GenerateKey(bPrompt:=False)
End Sub

Private Sub cmdGenKeyPrompt_Click()
    ' Generate a random key with user prompt
    Call GenerateKey(bPrompt:=True)
End Sub

Private Sub GenerateKey(bPrompt As Boolean)
    ' Generate a random key
    Dim sHexKey As String
    Dim nBytes As Long
    
    ' What length key do we need?
    If Me.optDES.Value Then
        nBytes = 8
    ElseIf Me.optTDEA.Value Then
        nBytes = 24
    ElseIf Me.optAES128 Then
        nBytes = 16
    ElseIf Me.optAES192 Then
        nBytes = 24
    ElseIf Me.optAES256 Then
        nBytes = 32
    Else
        ' Set to 16 byte length by default
        nBytes = 16
    End If
    
    ' Pad hex-encoded key to *DOUBLE* byte-length
    sHexKey = String(2 * nBytes, " ")
    If bPrompt Then
        Call RNG_HexWithPrompt(sHexKey, Len(sHexKey), nBytes, "", 0)
    Else
        Call RNG_KeyHex(sHexKey, Len(sHexKey), nBytes, "", 0)
    End If
    
    Me.optHexKey = True
    Me.txtKey = sHexKey
    Me.txtKeyAsString.Text = sHexKey
    ' Allow encrypt
    Me.cmdEncrypt.Enabled = True
    ' Put user in plaintext box
    Me.txtPlain.SetFocus

End Sub

Private Sub GenerateIV()
    ' Generate a random key
    Dim sHexIV As String
    Dim nBytes As Long
    
    ' What length IV do we need?
    nBytes = GetBlockLen()
    
    sHexIV = rngNonceHex(nBytes)
    
    Me.txtIV = sHexIV
    
End Sub

Private Sub cmdSetKey_Click()
    Call SetKey
    Call SetIV
End Sub
    
Private Function SetKey() As String
' Set key as a hex string
    Dim nKeyLen As Long
    Dim nPad As Long
    Dim strKey As String

    ' What format is the key in?
    If Me.optHexKey Then
        If IsValidHex(Me.txtKey) Then
            strKey = Me.txtKey
        Else
            MsgBox "Key is not valid hex"
            Me.txtKey.SetFocus
            GoTo Done
        End If
    Else
        ' User has provided a plain alpha string
        strKey = cnvHexStrFromString(Me.txtKey)
    End If
    
    ' Now pad key with zeroes as per algorithm
    If Me.optDES.Value Then
        nKeyLen = 8
    ElseIf Me.optTDEA.Value Then
        nKeyLen = 24
    ElseIf Me.optAES128 Then
        nKeyLen = 16
    ElseIf Me.optAES192 Then
        nKeyLen = 24
    ElseIf Me.optAES256 Then
        nKeyLen = 32
    Else
        nKeyLen = Len(strKey) \ 2
    End If
    
    ' Catch zero key length - default to 16 bytes
    If nKeyLen = 0 Then nKeyLen = 16
    
    ' nKeyLen is # of bytes - we want len of hex string
    nPad = nKeyLen * 2 - Len(strKey)
    If nPad > 0 Then
        strKey = strKey & String(nPad, "0")
    ElseIf nPad < 0 Then
        strKey = Left(strKey, nKeyLen * 2)
    End If
    
    ' Show key
    Me.txtKeyAsString = strKey
    ' Allow encrypt (and decrypt)
    Me.cmdEncrypt.Enabled = True
    Me.cmdDecrypt.Enabled = True
    ' Put user in plaintext box
    Me.txtPlain.SetFocus
    
    SetKey = strKey
    
Done:

End Function

Private Function SetIV() As String
' Set IV and show value in current IV box
    Dim strHexIV As String
    
    ' Do we need an IV? If not, clear current value
    If Not NeedIV() Then
        strHexIV = ""
        Me.txtIVAsString = strHexIV
        GoTo Done
    Else
        strHexIV = Me.txtIV
    End If
    
    ' Now pad IV with zeroes to match algorithm block length
    strHexIV = FixHexLength(strHexIV, GetBlockLen())
    
    ' Show IV
    Me.txtIVAsString = strHexIV
    
    SetIV = strHexIV
    
Done:

End Function

Private Function FixHexLength(strInputHex As String, nBytes As Long) As String
' Given a hex string, either pad with zeroes to make up to nBytes long or truncate.
    Dim nPad As Long
    
    nPad = nBytes * 2 - Len(strInputHex)
    If nPad > 0 Then
        FixHexLength = strInputHex & String(nPad, "0")
    ElseIf nPad < 0 Then
        FixHexLength = Left(strInputHex, nBytes * 2)
    Else
        FixHexLength = strInputHex
    End If

End Function

Private Function GetMode() As String
' Return the required string for the current mode
    If Me.optModeCBC Then
        GetMode = "CBC"
    ElseIf Me.optModeCTR Then
        GetMode = "CTR"
    Else    ' Default
        GetMode = "ECB"
    End If
End Function

Private Function NeedIV() As Boolean
' All modes except ECB require an IV
    NeedIV = (GetMode() <> "ECB")
End Function

Private Function NeedPadding() As Boolean
' We need padding if the user has asked for it AND we are in ECB or CBC mode
    Dim sMode As String
    sMode = GetMode()
    NeedPadding = (Me.optPad = True) And (sMode = "ECB" Or sMode = "CBC")
End Function

Private Function GetBlockLen()
' Return length in bytes of current algorithm's block size
' Note use of constants from basCryptoSys.bas
    If Me.optDES.Value Then
        GetBlockLen = API_BLK_DES_BYTES
    ElseIf Me.optTDEA.Value Then
        GetBlockLen = API_BLK_TDEA_BYTES
    ElseIf Me.optBLF.Value Then
        GetBlockLen = API_BLK_BLF_BYTES
    ElseIf Me.optAES128 Then
        GetBlockLen = API_BLK_AES_BYTES
    ElseIf Me.optAES192 Then
        GetBlockLen = API_BLK_AES_BYTES
    ElseIf Me.optAES256 Then
        GetBlockLen = API_BLK_AES_BYTES
    Else    ' default
        GetBlockLen = 8
    End If
End Function

Private Function IsValidHex(strToCheck As String)
' Returns True if strToCheck only contains valid hexadecimal digits
    Const scHEXDIGITS As String = "0123456789ABCDEFabcdef"
    ' NB Include both uc and lc just in case Binary Compare mode
    Dim i As Integer
    Dim nLen As Long

    IsValidHex = True
    nLen = Len(strToCheck)
    For i = 1 To nLen
        If InStr(scHEXDIGITS, Mid(strToCheck, i, 1)) = 0 Then
            IsValidHex = False
            Exit For
        End If
    Next
End Function

Private Sub HandleVBError()
' Display error message after unexpected VB error
    Dim sMsg As String
    sMsg = "VB error " & Err.Number & " has occurred:" & vbCrLf & vbCrLf & Err.Description
    MsgBox sMsg, vbCritical, "VB Error Handler"
End Sub

Private Sub DisplayCryptoSysError(nRet As Long)
    MsgBox "CryptoSys error " & nRet & " has occurred: " _
        & vbCrLf & vbCrLf & apiErrorLookup(nRet), vbExclamation, "CryptoSys Error"
End Sub