Bruce Schneier and the 448-bit Symmetric Key: Meet Blowfish

I know we mainly use AES and ChaCh20, but let’s wind back to a time before AES, and meet Blowfish.

Bruce Schneier and the 448-bit Symmetric Key: Meet Blowfish

I know we mainly use AES and ChaCh20, but let’s wind back to a time before AES, and meet Blowfish.

Bruce Schneier is a legend in Cybersecurity … possibly one of the most influential people in the industry. He mainly writes books and lectures these days, but, at one time, he created symmetric key encryption methods. The most famous of these is the Blowfish method. Like DES and 3DES, it uses a 64-bit block size (8 bytes), but unlike DES, it is unpatented [1]:

Overall it uses 16 Feistel-like iterations, and where the data input from the 64-bit block is split into two 32-bit words. An important strength of the method is that it can support key sizes up to 448 bits (56 bytes):

The Feistel cipher applies a symmetric key infrastructure and was named after Horst Feistel (IBM). It uses essentially the same encryption and decryption process, and where the key application is just reversed. The basic structure is given below and where we split the input data into blocks. Each block is then split into two (left and right). Each round is then:

The function applied (F) does not have to be reversible, which is unlike the case for AES. Also, in AES, we have an inverse function between the encryption and the decryption process, while a Feistel network just applies the key in the reverse order.

When NIST had their competition for AES, Bruce and others submitted the Twofish method. It used a 128-bit block size (as with AES) [2]:

Unfortunately, it was beaten by the Rindale method, and finished ahead of RC6 (Ron Rivest [4]) and MARS, while behind Rijndael and Serpent (Ross Anderson):

While Twofish did well for security, it possibly did too well and struggled against Rijndael for performance.

Coding

First we create a folder named “bc_ciphers_blowfish”, and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:

dotnet new console --framework net8.0

This produces a Csproject file of:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

We then add the latest Bouncy Castle library:

dotnet add package BouncyCastle.Cryptography --version 2.2.1

The following is the coding [here]:



namespace Blowfish
{
using System.Net;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

class Program
{

static void Main(string[] args)
{



var msg="Hello";

var iv="00112233445566778899AABBCCDDEEFF00";
var size=128;

if (args.Length >0) msg=args[0];
if (args.Length >1) iv=args[1];
if (args.Length >2) size=Convert.ToInt32(args[2]);



try {

var plainTextData=System.Text.Encoding.UTF8.GetBytes(msg);
var cipher= new BlowfishEngine();


byte[] nonce = new byte[8];
Array.Copy(Convert.FromHexString(iv), nonce, 8);



PaddedBufferedBlockCipher cipherMode = new PaddedBufferedBlockCipher(new CbcBlockCipher(cipher), new Pkcs7Padding());

CipherKeyGenerator keyGen = new CipherKeyGenerator();
keyGen.Init(new KeyGenerationParameters(new SecureRandom(), size));
KeyParameter keyParam = keyGen.GenerateKeyParameter();


cipherMode.Init(true,keyParam);
int outputSize = cipherMode.GetOutputSize(plainTextData.Length);
byte[] cipherTextData = new byte[outputSize];
int result = cipherMode.ProcessBytes(plainTextData, 0, plainTextData.Length, cipherTextData, 0);
cipherMode.DoFinal(cipherTextData, result);
var rtn = cipherTextData;


// Decrypt
cipherMode.Init(false,keyParam);

outputSize = cipherMode.GetOutputSize(cipherTextData.Length);
plainTextData = new byte[outputSize];

result = cipherMode.ProcessBytes(cipherTextData, 0, cipherTextData.Length,plainTextData, 0);

cipherMode.DoFinal(plainTextData, result);

var pln=plainTextData;


Console.WriteLine("=== {0} ==",cipher.AlgorithmName);
Console.WriteLine("Message:\t\t{0}",msg);
Console.WriteLine("Block size:\t\t{0} bits",cipher.GetBlockSize()*8);
Console.WriteLine("IV:\t\t\t{0}",iv);
Console.WriteLine("Key size:\t\t{0} bits",size);
Console.WriteLine("Key:\t\t\t{0} [{1}]",Convert.ToHexString(keyParam.GetKey()),Convert.ToBase64String(keyParam.GetKey()));


Console.WriteLine("\nCipher (hex):\t\t{0}",Convert.ToHexString(rtn));
Console.WriteLine("Cipher (Base64):\t{0}",Convert.ToBase64String(rtn));
Console.WriteLine("\nPlain:\t\t\t{0}",System.Text.Encoding.ASCII.GetString(pln).TrimEnd('\0'));



} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}



}

A sample run is [here]:

=== Blowfish ==
Message: Hello 123
Block size: 64 bits
IV: 00112233445566778899AABBCCDDEEFF00
Key size: 128 bits
Key: 2BC6E9A66692A026C261C983BC098F11 [K8bppmaSoCbCYcmDvAmPEQ==]

Cipher (hex): 08FA5244302598E03895F16DD9E7E892
Cipher (Base64): CPpSRDAlmOA4lfFt2efokg==

Plain: Hello 123

Conclusions

And, so Ross Anderson’s Serpent method was pipped at the post for AES, and so was Bruce’s method. The winner was the super flexible Rijndael method. And the rest is history.

References

[1] Schneier, B. (1993, December). Description of a new variable-length key, 64-bit block cipher (Blowfish). In International Workshop on Fast Software Encryption (pp. 191–204). Berlin, Heidelberg: Springer Berlin Heidelberg.

[2] Schneier, B., Kelsey, J., Whiting, D., Wagner, D., Hall, C., & Ferguson, N. (1998). Twofish: A 128-bit block cipher. NIST AES Proposal, 15(1), 23–91.

[3] Anderson, R., Biham, E., & Knudsen, L. (1998). Serpent: A proposal for the advanced encryption standard. NIST AES Proposal, 174, 1–23.

[4] Rivest, R. L., Robshaw, M. J., Yin, Y. L., & Alto, P. (2000, April). RC6 as the AES. In AES Candidate Conference (pp. 337–342).

[5] Burnwick, C., & Coppersmith, D. (1999). The MARS encryption algorithm. IBM, August, 27.