Attribute VB_Name = "basTDEABytesAndHex"
Option Explicit
Option Base 0

' $Id: basTDEABytesAndHex.bas $

' Examples showing how to encrypt a arbitrary-length 'text' string
' with CryptoSys API using both 'Bytes' and `Hex' modes.

' TDEA_Bytes_Example uses data in `Byte' format
' TDEA_Hex_Example uses data in `Hex' format

'Algorithm: Triple DES (TDEA, 3DES, des-ede3)
'Mode:      Cipher Block Chaining (CBC)
'Padding:   PKCS-7 Padding (same as PKCS-5)
'Key:       F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677
'IV:        FEDCBA9876543210
'Plaintext = "Hello world!"

' Copyright (C) 2006 DI Management Services Pty Limited.
' All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
' Last updated:
' $Date: 2006-08-18 06:39:00 $
' Use at your own risk.


Public Function TDEA_Bytes_Example()

' We are going to use the TDEA_BytesMode function in CBC mode
' which requires its data in Byte array form
' and requires the user to have provided the padding

    Dim strPlain As String
    Dim abKey() As Byte
    Dim abInitV() As Byte
    Dim abPlain() As Byte
    Dim abBlock() As Byte
    Dim abCipher() As Byte
    Dim strCheck As String
    Dim nPlainLen As Long
    Dim nBlockLen As Long
    Dim nRet As Long

' Key is required at 24 bytes long.
' Assume we are given the key, in hex format
' F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677
' We could set this the `long' way
'    ReDim abKey(23)     ' (0..23) = 24 bytes
'    abKey(0) = &HF0
'    abKey(1) = &HE1
'    ' ...
'    abKey(23) = &H77

' But we do a simple conversion using the cnvBytesFromHexStr function
    
    abKey = cnvBytesFromHexStr("F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677")
    
' As this is CBC mode we need an Initialization Vector (IV)
' of 8 bytes long selected randomly, e.g. FEDCBA9876543210 in hex.
' (NOTE: The IV should be different for each message and may be passed in the clear)

    abInitV = cnvBytesFromHexStr("FEDCBA9876543210")
    
    Debug.Print "KY=" & cnvHexStrFromBytes(abKey)
    Debug.Print "IV=" & cnvHexStrFromBytes(abInitV)
    
' E.1. Convert the plaintext string into Byte format
' We can use the built-in StrConv function to convert from 'text' to 'bytes'

    strPlain = "Hello world!"
    abPlain = StrConv(strPlain, vbFromUnicode)
    
    Debug.Print "SZ='" & strPlain & "'"
    Debug.Print "PT=" & cnvHexStrFromBytes(abPlain)
    
' E.2. Pad the message according to PKCS5/7 rules, namely:
' For a 64-bit block size:
' Append a padding string of between 1 and 8 bytes to make the total length an exact multiple of 8 bytes.
' The value of each byte of the padding string is set to the number of bytes added; namely,
' 8 bytes of value 0x08, 7 bytes of value 0x07, ..., 2 bytes of 0x02, or one byte of value 0x01.
' The length of the plaintext to be encrypted thus will be a multiple of 8 bytes and it will be
' possible to recover the message unambiguously from the decoded ciphertext.

' Version 3.2 of CryptoSys API introduced the PAD_BytesBlock and PAD_UnpadBytes functions
    nPlainLen = UBound(abPlain) - LBound(abPlain) + 1
    ' How long should our input block be?
    nBlockLen = PAD_BytesBlock(0, 0, abPlain(0), nPlainLen, API_BLK_TDEA_BYTES, 0)
    If nBlockLen <= 0 Then
        MsgBox "Unable to pad the plaintext. Error = " & nRet, vbCritical
        Exit Function
    End If
    ReDim abBlock(nBlockLen - 1)
    nBlockLen = PAD_BytesBlock(abBlock(0), nBlockLen, abPlain(0), nPlainLen, API_BLK_TDEA_BYTES, 0)
    If nBlockLen <= 0 Then
        MsgBox "Unable to pad the plaintext. Error = " & nRet, vbCritical
        Exit Function
    End If
    
    Debug.Print "IB=" & cnvHexStrFromBytes(abBlock)

' E.3. Call the encryption function in CBC mode
    
    nRet = TDEA_BytesMode(abBlock(0), abBlock(0), nBlockLen, abKey(0), ENCRYPT, "CBC", abInitV(0))
    
' It should return 0 to indicate success
    If nRet <> 0 Then
        MsgBox "Encryption failed. Error = " & nRet, vbCritical
        Exit Function
    End If
    
    Debug.Print "CT=" & cnvHexStrFromBytes(abBlock)
    
' OUTPUT the ciphertext

' The resulting ciphertext contains non-printable characters
' but we can look at it in hexadecimal form
    MsgBox "Ciphertext = " & cnvHexStrFromBytes(abBlock), vbOKOnly, "Encryption Output"
    
' TO DECRYPT, WE DO THE REVERSE PROCESS.

' D.1. First decrypt the ciphertext

    nRet = TDEA_BytesMode(abBlock(0), abBlock(0), nBlockLen, abKey(0), DECRYPT, "CBC", abInitV(0))
    If nRet <> 0 Then
        MsgBox "Decryption failed. Error = " & nRet
        Exit Function
    End If
    
    Debug.Print "OB=" & cnvHexStrFromBytes(abBlock)
    
' D.2. Remove the padding bytes

    ' We know that the unpadded data will be shorter than the output block
    ' so we can use our existing buffer and then shorten it
    nPlainLen = PAD_UnpadBytes(abBlock(0), nBlockLen, abBlock(0), nBlockLen, API_BLK_TDEA_BYTES, 0)
    ' This function checks for invalid padding bytes. If it fails, our decryption has failed.
    If nPlainLen < 0 Then
        MsgBox "Decryption failed. Error = " & nRet
        Exit Function
    End If
    ' Otherwise, it was successful, so re-dimension the buffer
    ReDim Preserve abBlock(nPlainLen - 1)

    Debug.Print "PT=" & cnvHexStrFromBytes(abBlock)
    
' D.3. Now we should have our plaintext, so convert back to a string
    
    strCheck = StrConv(abBlock, vbUnicode)
    
    Debug.Print "SZ='" & strCheck & "'"
    
' OUTPUT the plaintext string

    MsgBox "[" & strCheck & "]", vbOKOnly, "Decryption Output"


End Function

Public Function TDEA_Hex_Example()

' We are going to use the TDEA_HexMode function
' which requires its data as a hexadecimal-encoded string
' and requires the user to have provided the padding

    Dim nRet As Long
    Dim strKeyHex As String
    Dim strIVHex As String
    Dim strPlain As String
    Dim strPlainHex As String
    Dim strInputHex As String
    Dim strCipherHex As String
    Dim strOutputHex As String
    Dim strCheckHex As String
    Dim strCheck As String

' Set the key
    strKeyHex = "F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677"
    
' As this is CBC mode we need an Initialization Vector (IV)
' of 8 bytes long selected randomly, e.g.
' FEDCBA9876543210 in hex.
' (NOTE: The IV should be different for each message)

    strIVHex = "FEDCBA9876543210"
    
    Debug.Print "KY=" & strKeyHex
    Debug.Print "IV=" & strIVHex
    
' The plaintext to be encrypted

    strPlain = "Hello world!"
    
' E.1. Convert plaintext string to hexadecimal-encoded format

    strPlainHex = cnvHexStrFromString(strPlain)
    
    Debug.Print "SZ='" & strPlain & "'"
    Debug.Print "PT=" & strPlainHex

' E.2. Add padding bytes.
' CryptoSys API Version 3.2 introduces the PAD_HexBlock function
' and the VB wrapper function padHexString

    strInputHex = padHexString(strPlainHex, API_BLK_TDEA_BYTES)
    
    Debug.Print "IB=" & strInputHex
    
' E.3. Call the encryption function in CBC mode

' IMPORTANT: pre-dimension the output string first
    strCipherHex = String(Len(strInputHex), " ")
    
    nRet = TDEA_HexMode(strCipherHex, strInputHex, strKeyHex, ENCRYPT, "CBC", strIVHex)
    
' It should return 0 to indicate success
    If nRet <> 0 Then
        MsgBox "Encryption failed. Error = " & nRet
        Exit Function
    End If
    
    Debug.Print "CT=" & strCipherHex
    
' OUTPUT the ciphertext

' The hex-encoded ciphertext is printable (unlike the Byte version)
    MsgBox "Ciphertext = " & strCipherHex, vbOKOnly, "Encryption Output"
    
' TO DECRYPT, WE DO THE REVERSE PROCESS.

' D.1 Decrypt the ciphertext

' IMPORTANT: pre-dimension the output string first
    strOutputHex = String(Len(strCipherHex), " ")
    
    nRet = TDEA_HexMode(strOutputHex, strCipherHex, strKeyHex, DECRYPT, "CBC", strIVHex)
    If nRet <> 0 Then
        MsgBox "Decryption failed. Error = " & nRet
        Exit Function
    End If
    
    Debug.Print "OB=" & strOutputHex
    
' D.2. Remove the padding using wrapper function
    strCheckHex = unpadHexString(strOutputHex, API_BLK_TDEA_BYTES)
    
    ' If the result is the same length, the decryption has failed
    If Len(strCheckHex) = Len(strOutputHex) Then
        MsgBox "Decryption failed"
        Exit Function
    End If
        
    Debug.Print "PT=" & strCheckHex
    
' D.3. Convert to String format

    strCheck = cnvStringFromHexStr(strCheckHex)
    
    Debug.Print "SZ='" & strCheck & "'"
    
' OUTPUT the plaintext string
    MsgBox "[" & strCheck & "]", vbOKOnly, "Decryption Output"

End Function