The performance of various crypto ciphers

I recently stumbled over a gathering of people debating whether Twofish was a worse choice than AES, because “well, it said on Wikipedia that Twofish was slightly slower than AES”. It stood clear to me that the person bringing this argument was 1) only concerned about speedy crypto (and not so much about data safety – do we trust AES still?), 2) not aware of the different algorithmic complexities of these two ciphers, and 3) completely oblivious to the fact that depending on how much of a coder vs. software engineer jack-ass a developer is, his/her implementation will perform better or worse than that of another developer.

Without further ado, to quickly illustrate the point in question that ciphers can perform wildly different from one implementation to the other, I present you with a snippet of PHP code that utilizes the mcrypt library:

<?php

// pseudo-random binary data
function rnd( $length )
{
    while( @$i++ * 16 < $length )
        @$tmp .= md5( mt_rand(), true );
    return substr( $tmp, 0, $length );
}

// our suite of cipher setups to test, incl. extra ciphers for good measure
$suite = array( ' Twofish/256'=>array('c'=>MCRYPT_TWOFISH, 'key'=>32, 'iv'=>16),
                '     AES/128'=>array('c'=>MCRYPT_RIJNDAEL_128, 'key'=>16, 'iv'=>16),
                '     AES/256'=>array('c'=>MCRYPT_RIJNDAEL_256, 'key'=>32, 'iv'=>32),
                '    3DES/168'=>array('c'=>MCRYPT_3DES, 'key'=>21, 'iv'=>8),
                ' Serpent/256'=>array('c'=>MCRYPT_SERPENT, 'key'=>32, 'iv'=>16),
                'Blowfish/448'=>array('c'=>MCRYPT_BLOWFISH, 'key'=>56, 'iv'=>8) );

/* we run the entire cipher suite twice; first with 50 1mb blocks and then with
   3200 16kb blocks, just to see how each cipher's preparatory key schedule
   affects its overall performance */
$sizes = array( '50'=>rnd(1024*1024), '3200'=>rnd(16*1024) );
$key = rnd( 56 );
$iv = rnd( 32 );

// here we go
foreach( $sizes as $times => $data )
{
    echo "\n$times runs of " . strlen( $data ) . " bytes each\n---\n";
    foreach( $suite as $descr => $cipher )
    {
        echo "$descr\t";
        $k = substr( $key, 0, $cipher['key'] );
        $i = substr( $iv, 0, $cipher['iv'] );
        $count = $times;
        $then = microtime( true );
        while( $count-- )
            $enc = mcrypt_encrypt( $cipher['c'], $k, $data, MCRYPT_MODE_CBC, $i );
        printf( "%.4fs\n", microtime(true) - $then );
    }
}

?>

Let’s see how this goes on my server machine, equipped with a 1ghz Sempron running OpenBSD 4.8/i386 with PHP 5.3.3 and libmcrypt 2.5.8, all built with GCC 4.2.1:

50 runs of 1048576 bytes each
---
 Twofish/256    2.9234s
     AES/128    7.5719s
     AES/256    9.6267s
    3DES/168    28.6203s
 Serpent/256    4.3912s
Blowfish/448    3.1937s

3200 runs of 16384 bytes each
---
 Twofish/256    4.1996s
     AES/128    8.5839s
     AES/256    11.9875s
    3DES/168    32.6580s
 Serpent/256    6.4841s
Blowfish/448    6.1409s

We can instantly see that in the libmcrypt implementation, Twofish performs more than twice as fast as AES/128, and more than three times as fast as AES/256. Building upon this, we can also see that in this case Twofish even performs faster than Blowfish, despite it being far simpler algorithmically. And lastly, there is the interesting detail in the second run of tests, in how the various ciphers’ key schedules make themselves reminded – in particular the highly intricate key schedule of the Blowfish cipher.

This entry was posted in Cryptography, Programming and tagged , , . Bookmark the permalink.

Leave a comment