phpseclib: Symmetric Key Encryption Examples and Notes


Encryption Mode:


Key Length:

Key Length:

Block Length:

Key Derivation:


$cipher = new Crypt_AESRijndaelDESTripleDESRC4TwofishBlowfishRC2(
// keys are null-padded to the closest valid size
// longer than the longest key and it's truncated
// keys can range in length from 32 bits to 448 in steps of 8
// keys can range in length from 8 bits to 1024 in steps of 8
// the following does the same thing:
//$cipher->setPassword('whatever', 'pbkdf2', 'sha1', 'phpseclib/salt', 1000, 128 / 8, 128192256 / 8, 128160192224256 / 8);
// the IV defaults to all-NULLs if not explicitly defined
$cipher->setIV(crypt_random_string($cipher->getBlockLength() >> 3));

$size = 10 * 1024;
$size-= 16; // make the plaintext length a multiple of the block length
$size-= 16; // make the plaintext length a multiple of the block length
$size-= 4; // make the plaintext length a multiple of the block length
$plaintext = str_repeat('a', $size);

echo $cipher->decrypt($cipher->encrypt($plaintext));

// without continuous mode the following would yield the same output as the above
// with continuous mode it's treated the same way str_repeat('a', 2 * $size) would be
$plaintext = str_repeat('a', $size);

echo $cipher->decrypt($cipher->encrypt($plaintext));

A departure from mcrypt

In theory, encrypting a string using a stream cipher should yield the same results no matter how that string is partitioned out. At least when enableContinuousBuffer() has been called. This is not the case with mcrypt. The following link demonstrates how phpseclib's rewritten CFB implementation (CFB mode is one of several modes that turn block ciphers into stream ciphers) guarantees this behavior:

Word of caution about stream ciphers

Most stream ciphers (and block ciphers operating in a mode - like CTR, CFB and OFB - that turns them into stream ciphers) work by generating a stream of pseudorandom characters called a keystream and then XOR'ing that with the plaintext. This *effectively* makes them one-time pads which, in theory, can provide perfect secrecy. The problem with one-time pads is that they're not as versatile as one might desire. Among other things, a keystream must never be reset, lest it be possible for an attacker to recover the keystream via a known-plaintext attack. ie. $ciphertext ^ $plaintext = $key. If $key is constant (because the keystream's being reset or something) than an attacker can recover any $plaintext, but if not - if it's dynamic - then the only key that an attacker could recover is their own.

Inner Chaining vs. Outer Chaining

Inner chaining, as used by SSH1, does DES three times on the whole string. Outer chaining, as used by SSH2 and most other 3DES implementations, does DES three times on each block. Outer chaining is generally considered more secure.

RC2 is a Block Cipher

Unlike RC4, RC2 is a block cipher.

Speed Comparisons

phpseclib uses OpenSSL or mcrypt (in that order) if they're available and a pure-PHP implementation when it is not. Neither OpenSSL or mcrypt can be beaten in terms of speed by a pure-PHP implementation, however, as the following demonstrates, even phpseclib's pure-PHP implementation is surprisingly fast.

The following table compares the speed of five different pure-PHP implementations of AES when ran on 1MB of text on an Intel Core i5-3320M CPU @ 2.6GHz with PHP 5.5.3. The numbers listed are averaged from two different trials and are measured in seconds. phpseclib's implementation is highlighted. All implementations can be viewed by clicking on their names and the table was generated with benchmark.phps.

Implementation Speed
movable-type.phps 21.164710998535
phpaes.phps 37.811162352562
phpclasses1.phps 17.648010134697
phpclasses2.phps 43.407482504844
phpseclib.phps 1.0410599708557

As can be seen, phpseclib's implementations are the fastest. Note that if mcrypt and OpenSSL weren't explicitily disabled phpseclib would have been even faster.