Io Programming/Addons and You/Blowfish
What is blowfish?
First of all, what exactly is Blowfish?
“Blowfish is a symmetric block cipher that can be used as a drop-in replacement for DES or IDEA. It takes a variable-length key, from 32 bits to 448 bits, making it ideal for both domestic and exportable use. Blowfish was designed in 1993 by Bruce Schneier as a fast, free alternative to existing encryption algorithms. Since then it has been analyzed considerably, and it is slowly gaining acceptance as a strong encryption algorithm. Blowfish is unpatented and license-free, and is available free for all uses.” — http://www.schneier.com/blowfish.html
At the time of this writing it is a 17-year-old piece of encryption code that to date remains unbroken, and though it has a superseding algorithm (Twofish) that is recommended for newer applications’ use, Blowfish still remains reasonably secure for applications old and new alike.
A Blowfish addon is shipped with Io source distributions and has no external dependencies, so it will be available if you have compiled the addons with the Io interpreter and your interpreter supports loading addons to begin with. If you followed the standard build procedures for Io on your platform or downloaded a binary package, you most likely have access to the Blowfish object.
When to encrypt
Note that encryption can be a cumbersome action as it adds an additional step to any data processing pipeline, you must have some way to safely get and handle encryption keys and in certain countries there are legal ramifications for handling and distributing encrypted data. That being said, there are times where the potential complexity is worth it. You should ideally use encryption for sensitive data such as:
- If you are storing credit card information for a user on their machine.
- If you are storing financial information for a user on their machine.
- If you are passing private information over the network or otherwise in such a way where other users may have access to the data, but are not those who are meant to read it.
- For authentication purposes, such as passing tokens between authentication servers and service rendering servers. For an example of this kind of authentication, read up on the Kerberos protocol.
- At the request of the user for when they input data that they feel is important enough to protect.
It is also important to keep in mind that no encryption algorithm or practices are 100% safe, and that data is only made extremely difficult (but not impossible) to read by unauthorized persons. Security researchers and hardware advances can make any algorithm that was once believed to be “impenetrable” easily cracked given enough time and dedication, and intelligence agencies may have broken algorithms in common use without disclosing that they have been able to do so. Note that encryption is not useful if you are intending to keep data from a user who is also meant to be able to interact with the data in some way, and will generally only serve to annoy the end user if you try to use this in such a way to implement some form of Digital Restrictions Management.
Preparing to Encrypt
To encrypt or decrypt data, you will need to create a Blowfish object and set the encryption key to a Sequence that will be used. This Sequence needs to be the same for the decryption object as it is for the encryption object for the decrypted data to make any sense, keep this in mind when transmitting or storing data. Note that storing the encryption key in the script will defeat the purpose of using encryption for local storage to begin with, unless you can take tight control over who will be able to read the contents of the script with file permission settings. Start by opening up a text file (which we will call “blowfish_test.io” for reference) and entering the following text in to it:
encryptor := Blowfish clone decryptor := Blowfish clone
Then, run the file in a terminal:
$ io blowfish_test.io
If the result looks something like this:
Exception: Object does not respond to 'Blowfish' --------- Object Blowfish Command Line 1
Then you do not have the Blowfish addon installed. If there is no output from running the command, then you do have the addon installed and the script ran successfully. Assuming everything worked out all right we need to add a secret key in order for any of this to be at all useful, after all there would be no point in encryption if just anybody could open the message up without any effort would there? :) Add the following code to your script now:
key := "secret" encryptor setKey(key) decryptor setKey(key)
This sets both the encryption and decryption object to use the same key, so the decryption object will be able to make sense of the data output by the encryption object. There are two ways of encrypting or decrypting data with the Blowfish addon: all at once, or streaming. Both of these methods are covered in this guide.
Encrypting/Decrypting All at Once
To encrypt an entire Sequence at once, simply send the
encrypt message to a Blowfish object with its encryption key set like this:
encryptedText := encryptor encrypt("This is a very secret sentence.")
To decrypt an entire Sequence at once, you do the same thing but the message is called
decryptedText := decryptor decrypt(encryptedText)
Encrypting/Decrypting a Stream
When using a Blowfish object in streaming mode you need to first set whether the stream will be encrypting or decrypting, and you do this with the
setIsEncrypting message which is
true for an encrypting stream and
false for a decrypting stream:
encryptor setIsEncrypting(true) decrypting setIsEncrypting(false)
The way this works is there is both an
outputBuffer slot which are Sequences, you first send
beginProcessing to prepare the stream and then subsequently you use
appendSeq to place encrypted or unencrypted chunks in to the
inputBuffer and then send the
process message periodically in order to have as much data from the input buffer encrypted and placed in the output buffer as can be done with the given data. Because of the way Blowfish works in encrypting fixed-sized blocks anything smaller than the expected block size doesn't get encrypted when you run
process, when you have finished piping in all of your data to be encrypted or decrypted you simply send
endProcessing causes ANY remaining data to be encrypted or decrypted and simply pads the data as needed to make it work, this signifies the end of the stream. See this example:
encryptor beginProcessing encryptor inputBuffer appendSeq("Hello, I am a toaster.") encryptor process encryptor inputBuffer appendSeq(" I am another toaster.") encryptor endProcessing encryptor outputBuffer println # this should print what looks like garbage