DI Management Home > Cryptography > Blowfish in Classic Visual Basic VB6

Blowfish in Classic Visual Basic VB6


This page comments on the changes in (our) version 6 of our code for Blowfish in Classic Visual Basic (VB6/VBA). Our original Blowfish in VB was first published in October 2000. Version 6 of the code was released in November 2003. To download the source code, go to Blowfish: a Visual Basic version.

Strings vs Bytes

In previous versions of our Blowfish Visual Basic code, we used the VB 'String' types to store the data before and after encryption. We developed this on a W98 computer set up for the ordinary US-ASCII character set and it all worked OK, well for us anyway. To carry out encryption we had the blf_StringEnc() and blf_StringDec() functions which worked as follows
strCipher = blf_StringEnc(strPlain)

We now acknowledge that this is not the correct way to store binary data and may lead to problems on systems set up for full 32-bit Unicode or oriental CJK character sets. The input to and output from any encryption algorithm is a bit string and the safest way to ensure that all the bits stay exactly as you want them is to use arrays of the VB 'Byte' type.

Sure, you can use a 'String' type to store binary values and you can treat each individual character as an 8-bit byte if your system is set up in ANSI. After all, Strings are easier to create and manage in VB. But it doesn't necessarily work across all system setups.

What we should do

All operations on 'binary' data should carried out using the Visual Basic Byte type, and all textual data (original text input, password and base64-encoded data) should be stored in the VB String type. It is important to make sure you differentiate between these two types of data - broadly speaking 'text' and 'binary' - when doing cryptographic operations in Visual Basic.

'Text' consists of readable, printable characters we expect to see on our computer screen or in a book. It might consist of simple US-ASCII/ANSI characters or it could be Unicode or DBCS oriental character strings. 'Binary' data is a string of bits that we conventionally store as bytes or octets.

Binary data must be exactly the same literally bit-for-bit in all systems. Change one bit and the results of any cryptographic operation on it will be completely different. The way to ensure our binary data is always the same is to use the Byte type to store binary data, not the String type.

Text data may be stored differently depending on the particular system we are using. On a ANSI system, each character is stored in one byte. On a Unicode system each character is stored in two bytes; and on a DBCS system, a character may be stored in one or two bytes. It is important to make sure we always convert our 'text' data into exactly the same 'binary' form before we do any encryption.

The input to an encryption process must be 'binary' data. We need to convert the text we want to encrypt into 'binary' format first and then encrypt it. The results of encryption are always binary. Do not attempt to treat raw ciphertext as 'text' or put it directly into a String type. Store ciphertext either as a raw binary file or convert it to base64 or hexadecimal format. You can safely put data in base64 or hexadecimal format in a String.

When you decrypt, always start with binary data, decrypt to binary data, and then and only then, convert back to text, if that is what you are expecting. You can devise your own checks to make sure the decrypted ciphertext is what you expect before you do the final conversion, perhaps by checking that the first few bytes are valid printable ASCII characters in the range 0x20 to 0x7e (plus the control characters TAB (0x09), CR (0x13) and LF (0x10)).

On a US-English system set up for ANSI characters, you can probably get away with using a String type to carry out 'binary' operations. However you will encounter problems on a system set up for Chinese/Japanese/Korean/Hebrew/Arabic characters.

Changes in Version 6

We have replaced the two blf_StringEnc() and blf_StringDec() functions with new blf_BytesEnc() and blf_BytesDec() functions that work with arrays of Bytes.

The onus is now on the user to convert their textual plaintext data into an unambiguous array of bytes before carrying out encryption and back again after decryption.

The first example shows how to encrypt some simple ASCII plaintext using the Visual Basic StrConv function to convert the String into an array of Bytes before encryption.

    Dim strData As String
    Dim strResult As String
    Dim abBytes() As Byte
    Dim abKey() As Byte
    
    ' Load test key and initialise
    abKey() = cv_BytesFromHex("FEDCBA9876543210")
    Call blf_KeyInit(abKey)
    Debug.Print "KY=" & cv_HexFromBytes(abKey)
    
    strData = "Hello, world!"
    ' Convert to byte array
    abBytes = StrConv(strData, vbFromUnicode)
    Debug.Print "PT=" & cv_HexFromBytes(abBytes)
    
    ' Encrypt
    abBytes = blf_BytesEnc(abBytes)
    Debug.Print "CT=" & cv_HexFromBytes(abBytes)
    
    ' Decrypt
    abBytes = blf_BytesDec(abBytes)
    Debug.Print "P'=" & cv_HexFromBytes(abBytes)
    
    ' Convert back to a string
    strResult = StrConv(abBytes, vbUnicode)
    Debug.Print strResult
This should produce the following output in the Immediate Window:
KY=FEDCBA9876543210
PT=48656C6C6F2C20776F726C6421
CT=AD57555135819EEC189034F3D753258D
P'=48656C6C6F2C20776F726C6421
Hello, world!
This second example shows how to encrypt the same string but this time it converts the VB 'String' directly without Unicode conversion. Remember that VB always stores all its strings internally as 16-bit (two-byte) Unicode characters. It then goes to great lengths to hide this fact from us, until, of course, we want to do bit-string manipulation on them.
    Dim strData As String
    Dim strResult As String
    Dim abBytes() As Byte
    Dim abKey() As Byte
    
    ' Load test key and initialise
    abKey() = cv_BytesFromHex("FEDCBA9876543210")
    Call blf_KeyInit(abKey)
    Debug.Print "KY=" & cv_HexFromBytes(abKey)
    
    strData = "Hello, world!"
    ' Convert to byte array without Unicode conversion
    abBytes = strData
    Debug.Print "PT=" & cv_HexFromBytes(abBytes)
    
    ' Encrypt
    abBytes = blf_BytesEnc(abBytes)
    Debug.Print "CT=" & cv_HexFromBytes(abBytes)
    
    ' Decrypt
    abBytes = blf_BytesDec(abBytes)
    Debug.Print "P'=" & cv_HexFromBytes(abBytes)
    
    ' Convert back to a string
    strResult = abBytes
    Debug.Print strResult
The results of this will be:
KY=FEDCBA9876543210
PT=480065006C006C006F002C00200077006F0072006C0064002100
CT=6BDCB304F8CFEFB462891E00115773D304C4F06AFF91401EB7BD5598E2B2885E
P'=480065006C006C006F002C00200077006F0072006C0064002100
Hello, world!

ANSI vs Unicode

Note how changing just two lines in the code produced two completely different results for the ciphertext:
CT=AD57555135819EEC189034F3D753258D
CT=6BDCB304F8CFEFB462891E00115773D304C4F06AFF91401EB7BD5598E2B2885E
and that was for an identical string of plain text "Hello, world!".

In the first example the string "Hello, world!" of 13 characters was converted into exactly 13 bytes where "H"=0x48, "e"=0x65, "l"=0x6C, ..., "!"=0x21. The encryption function padded this to 16 bytes (the next highest multiple of the Blowfish block size of 8 bytes) and then encrypted it. This sort of ANSI-to-byte conversion was implicit in our now deprecated use of blf_StringEnc() functions.

In the second example, the string of 13 characters is converted into its Unicode equivalent where each character is a 2-byte value "H"=0x0048, "e"=0x0065, ..., "!"=0x0021 (stored, you will note, in little-endian order 0x48006500...). This gives us 26 bytes of plaintext to encrypt. The Blowfish encryption function doesn't care what the bytes represent; it just sees a string of bits that we have stored conveniently in 8-bit containers. Provided we convert back to a 'String' in the reverse manner after decryption, we end up where we started.

More Information

For more information on dealing with Unicode and ANSI characters sets see Cryptography with International Character Sets.

See also

Back to the Cryptography Page.

Contact

Email Us

This page last updated 28 August 2008