Crypto class 0.3

A bit of an overhaul to my old cryptography class, I thought I’d post it before it goes into my side forum project. This includes functions for password hashing (Bcrypt and PBKDF2) as well as your garden variety encryption helpers for sessions, cookies and such. Questions, comments, corrections (especially corrections) feel free to comment.

Side note: I’ve taken to using the encode/decode functions here in lieu of standard base64_encode/decode in PHP. It’s only in ECB mode so it’s not secure, but plain base64 was leaving things just a little too easy for Peeping Toms.

As always, this comes with no warranties, liabilities, no guarantees, thar be dragons etc…

I’ll post more of the actual forum code when I get a chance.

<?php

/**
 * Encryption/Decryption, Encoding/Decoding and password related functions.
 *
 * The license below was inherited from the project this originally went into.
 * If you need a different license for some reason, just drop me a line.
 *
 * @author Eksith Rodrigo <reksith at gmail.com>
 * @license http://opensource.org/licenses/ISC ISC License
 * @version 0.3
 */

class Crypto {
	
	
	/**#@+
	 * Instance getter, constructor and destructor
	 */
	
	private static $_instance;
	public static function getInstance() {
		if ( !self::$_instance instanceof self ) {
			self::$_instance = new self();
		}
		
		return self::$_instance;
	}
	
	private function __construct() {}
	
	/**#@-*/
	
	
		
	/**#@+
	 * Password related functions
	 */
	
	
	/**
	 * Original PBKDF2 code courtesy of havoc : https://defuse.ca/php-pbkdf2.htm
	 * Adapted here for customized functionality
	 */
	public function create_pbkdf2( $pass, $algo, $rounds, $salt, $klen = 30 ) {
		if ( empty( $salt ) ) {
			$salt = base64_encode( $this->IV( $klen ) );
		}
		
		$hash = $this->pbkdf2( $algo, $pass, $salt, $rounds, $klen );
		
		return "$algo:$rounds:$salt:$hash"; 
			
	}
	
	public function validate_pbkdf2( $pass, $stored, $fast = false ) {
		$args = explode( ':', $stored );
		if ( count( $args ) < 4 ) { return false; }
		
		$p = $this->pbkdf2( $ars[0], $pass, $args[2], ( int ) $args[1], strlen( $args[3] ) );
		
		// Don't use fast for passwords!
		if ( $fast ) {
			return strcmp( $h, $p );
		}
		
		return $this->slow_cmp( $h, $p );
	}
	
	public function slow_cmp( $a, $b ) {
		$d = strlen( $a ) ^ strlen( $b );
		for( $i = 0; $i < strlen( $a ) && $i < strlen( $b ); $i++ ) {
			$d |= ord( $a[$i] ) ^ ord( $b[$i] );
		}
		
		return 0 === $d;
	}
	
	
	/**
	 * PBKDF2
	 *
	 * @param string $algo Hash alorithm ( one from hash_algos() )
	 * @param string $pass Unhashed original password
	 * @param string $salt Random semi-unique salt
	 * @param int $round Number of rounds to hash
	 * @param int $kl Hashed key length 
	 * 		Note: this will be smaller than the final pass size when used 
			with the above two related functions depending on salt length
	 * @param bool $raw Hash return mode
	 */
	public function pbkdf2( $algo, $pass, $salt, $rounds, $kl, $raw = false ) {
		if ( !in_array( $algo, hash_algos(), true ) ) {
			return null;
		}
		
		if ( $rounds <= 0 || $kl <= 0 ) {
			return null;
		}
		
		$hl = strlen( hash( $algo, '', true ) );
		$bl = ceil( $kl / $hl );
		
		$out = '';
		
		for ( $i = 1; $i <= $bl; $i++ ) {
			$last = $salt . pack( "N", $i );
			$last = $xor = hash_hmac( $algo, $last, $pass, true );
			for ( $j = 1; $j < $rounds; $j++ ) {
				$xor ^= ( $last = hash_hmac( $algo, $last, $pass, true ) );
			}
			$out .= $xor;
		}
		
		if ( $raw ) {
			return substr( $out, 0, $kl );
		}
		
		return bin2hex( substr( $out, 0 , $kl ) );
	}
	
	/**
	 * Hash password with blowfish(14 rounds) + salt
	 * 
	 * @param string $user Username
	 * @param string $pass Raw password
	 * @param string $salt Stored salt after being retreived or freshly generated
	 */
	public function password( $user, $pass, $salt ) {
		// This will reject NULL, 0, FALSE etc... but these are terrible passwords anyway
		if ( empty( $user ) || empty( $pass ) || empty( $salt ) ) {
			return false;
		}
		
		return crypt( $pass, '$2y$14$' . $salt . $user . '$' );
	}
	
	
	/**
	 * Verify password hash against the stored value
	 * 
	 * @param string $user Username
	 * @param string $pass Raw password supplied by the user
	 * @param string $salt Password salt as stored in the database
	 * @param string $storedPass Hashed password to match against
	 */
	public function verifyPassword( $user, $pass, $storedPass, $salt ) {
		if ( empty( $user ) || empty( $pass ) || empty( $salt ) || 
			empty( $storedPass ) ) {
			return false;
		}
		
		$pass = $this->password( $user, $pass, $salt );

		if ( $storedPass === $pass ) {
			return true;
		}
		return false;
	}


	/**
	 * Hashes the passed password and creates a matching salt (also encoded)
	 * This function does NOT create a new password.
	 *
	 * @param string $user Username
	 * @param string $pass User provided password
	 * @param string $salt Generated salt
	 */
	public function pairPasswordSalt( $user, &$pass, &$salt ) {
		if ( empty( $pass ) ) {
			return false;
		}

		$salt = bin2hex( $this->IV( 30, true ) );
		$pass = $this->password( $user, $pass, $salt );

		return true;
	}
	
	/**#@-*/
	
	
	
	/**#@+
	 * Random number/string generators
	 */
	
	/**
	 * Generates a random string/password of a given length
	 * (or a random length between 8 and 12 if no length is given)
	 *
	 * @param int $len Random string length. If empty, will revert to a number between 8 - 12
	 * @return string Alphanumeric string
	 */
	public function randomStr( $len = 0, $pass = false ) {
		if ( empty( $len ) ) {
			$len = $this->_rnd( 8, 12 );
		}

		$r = base64_encode( $this->IV( 30, true ) );
		$r = str_replace( array( '+', '/', '-', '_', '=' ), '', $r );
		
		if ( $pass ) {
			// If this is a password, remove confusing chars
			$r = str_replace( array( '1', 'i', 'l', '0', 'o', '5', 's' ), '', $r );
		}
		
		if ( strlen( $r ) > $len ) {
			return substr( $r, 0, $len );
		}
		return $r;
	}
	
	
	/**
	 * Generates a random number
	 *
	 * @param int $min Minimum number size. Defaults to 1
	 * @param int $max Maximum number size. Defaults to maximum allowed in 32 bit PHP
	 */
	public function randomInt( $min = 1, $max = 0x7FFFFFFF ) {
		$d = $max - $min;
		if ( $d < 0 || $d > 0x7FFFFFFF ) {
			$d = 1;
		}

		$r = $this->IV( 4, true );
		$p = unpack( "Nint", $r );
		$f = ( float ) ( $p['int'] & 0x7FFFFFFF ) / 2147483647.0;

		return  round( $f * $d ) + $min;
	}
	
	
	/**
	 * Generates a random IV for encryption and password/string generation
	 *
	 * @returns mixed Binary IV
	 */
	public function IV( $size, $ssl = false ) {
		if ( $ssl && function_exists( 'openssl_random_pseudo_bytes' ) ) {
			return openssl_random_pseudo_bytes( $size, true );
		}
		return mcrypt_create_iv( $size, MCRYPT_DEV_URANDOM );
	}
	
	/**#@-*/
	
	
	
	/**#@+
	 * Encryption
	 */
	
	
	/**
	 * Encrypt and serialize data with a given key
	 *
	 * @param mixed $data Data to encrypt (assumes strings are UTF-8)
	 * @param string $key Encryption key
	 * @return string Serialized and encrypted
	 */
	public function cryptSerialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = json_encode( $data );
			return $this->encryption( $data, $key, 'encrypt' );
		}
	}
	
	
	/**
	 * Deserialize and decrypt data
	 * This is safer than PHP's 'deserialize' especially with cookie data
	 *
	 * @param string $data Data to decrypt (this should be completely unaltered)
	 * @param string $key Decryption key
	 * @return mixed Orignally serialized object
	 */
	public function cryptDeserialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = $this->encryption( $data, $key, 'decrypt' );
			if ( empty( $data ) ) {
				return '';
			} else {
				try {
					return json_decode( $data );
				} catch ( Exception $e ) {
					return '';
				}
			}
		}
	}
	
	
	/**
	 * Encode ( so called because this is only ECB ) in Rijndael 256 with Base64 encoding
	 *
	 * @param string $data The content to be encoded
	 * @param string $salt Required nonce
	 * @return string Encoded and optionally base64 encoded string
	 */
	public function encode( $data, $salt ) {
		$enc = mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		return base64_encode( $enc );
	}
	
	
	/**
	 * Decode as encoded above
	 *
	 * @param string $data The content to be decoded
	 * @param string $salt Required matching nonce to the encrypted data
	 * @return string Decoded content
	 */
	public function decode( $data, $salt ) {
		$data = base64_decode( $data );
		$dec = mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		
		// Padding is automatic, but unpad is not
		return $this->_unpad( $dec, 32 );
	}
	
	
	/**
	 * Encrypt/Decrypt in Rijndael 256 with Base64 encoding/decoding
	 *
	 * @param string $str The content to be encrypted/decrypted
	 * @param string $key Encryption key
	 * @param string $mode Function mode ( 'encrypt' / 'decrypt' )
	 * @return string Encrypted or decrypted content
	 */
	public function encryption( $str, $key, $mode = 'encrypt' ) {
		// Open Rijndael-256 in CBC mode
		$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
		
		// Find the IV size for the mcrypt module
		$size = mcrypt_enc_get_iv_size( $td );
		
		// Block sizes are needed to calculate the padding ( further down )
		$bsize = mcrypt_enc_get_block_size( $td );
		
		// Key size
		$ksize = mcrypt_enc_get_key_size( $td );
		
		// Hashed key ( for consistency )
		$hkey = hash( 'ripemd160', $key );
		
		// Hash the key for consistency and use only the key size needed from the front
		$key = substr( $hkey, 0, $ksize );
		
		// Salt for encoding/decoding is extracted from the back of the key hashed again
		$salt = substr(  hash( 'ripemd160', $hkey ), ( -1 * $ksize ) );
		
		if ( $mode == 'encrypt' ) {
			/// We're encrypting. Create a new initialization vector
			$iv = $this->IV( $size );
		} else {
			// Unwrap the combined IV and data packet and extract the IV from the front (using IV size)
			$str = $this->decode( $str, $salt );
			$iv = mb_substr( $str, 0, $size );
			
			// Isolate the data by removing the IV altogether and decode in preparation for decryption
			$str = mb_substr( $str, mb_strlen( $iv ) );
			$str = base64_decode( $str );
		}
		
		// Initialize mcrypt
		mcrypt_generic_init( $td, $key, $iv );
		
		if ( $mode == 'encrypt' ) {
			// Prepare string by padding to match block size and encrypt
			$str = $this->_pad( $str, $bsize );
			$enc = mcrypt_generic( $td, $str );
			
			// Add the IV to the front and Encode
			$out = $this->encode( $iv . base64_encode( $enc ), $salt );
		} else {
			// Decrypt the data ( we removed the IV and decoded above ) and remove padding
			$str = mdecrypt_generic( $td, $str );
			$out = $this->_unpad( $str, $bsize );
		}
		
		// Clean up
		mcrypt_generic_deinit( $td );
		mcrypt_module_close( $td );
		
		// Return encrypted/decrypted string
		return $out;
	}
	
	/**#@-*/
	
	
	
	/**#@+
	 * Helpers
	 */
	
	
	/**
	 * Pad data to encryption block size.
	 * Michael Corleone says hello.
	 */
	private function _pad( $str, $bsize ) {
		// Find the pad size for this block size and string length
		$pad = $bsize - ( mb_strlen( $str ) % $bsize );
		
		// Repeat the equivalent character up to the pad size
		$str .= str_repeat( chr( $pad ), $pad );
		
		return $str;
	}
	
	
	/**
	 * Remove extra padding added during encryption.
	 * This is a bit of a hack, so if you have improvements, please add them [ and let me know :) ].
	 * Thanks!
	 */
	private function _unpad( $str, $bsize ) {
		$len = mb_strlen( $str );
		
		// Find the pad character ( last one )
		$pad = ord( $str[$len - 1] );

		// If padding would have been applied to the string...
		if ($pad && $pad < $bsize) {
			// ...find the pad
			$pm = preg_match( '/' . chr( $pad ) . '{' . $pad . '}$/', $str );

			// Pad found, strip it.
			if( $pm ) {
				return mb_substr( $str, 0, $len - $pad );
			}
		}
		return $str;
	}
	
	
	/**
	 * Workaround for mt_rand abnormalities
	 */
	public function _rnd( $min, $max ) {
		$r = 0;
		if ( $min > $max ) {
			$min ^= $max;
			$max ^= $min;
			$min ^= $max;
		}
		while( 0 === $r || $r < $min || $r > $max ) {
			$r = mt_rand( $min, $max );
		}
		
		return $r;
	}
	
	/**#@-*/
}

DIY Hashing (if you must)

Every so often, I come across more and more clever schemes implemented by programmers wanting to one-up on what’s already available in their favorite software libraries. My response is DON’T DO IT! For one very simple reason: Crypto is hard. Good crypto is harder. PHP developers in particular are notorious for this, but I’ve seen C# devs do the same.

Even if you come up with a clever hashing scheme of your own, you don’t really want to play around next to something that’s tried and proven, like say bcrypt. Plus there are innumerable instances where very, very smart people have gone out and created their own schemes, only to be defeated by an attack a short while later. It happens. That’s reality.

Consider password hashing

You shouldn’t use anything other than bcrypt. There I said it. There are many examples on the web on how to use bcrypt, but just in case you’re wondering, here are couple of functions :

/**
 * Hash password with blowfish + salt
 *
 * @param string $pass Raw password
 * @param string $salt Stored salt stored in the db or generated
 */
public function password( $pass, $salt ) {
	if ( empty( $pass ) || empty( $salt ) ) {
		return false;
	}
	return crypt( $pass, '$2y$09$'. $salt . '$' );
}

/**
 * Verify password hash against the stored value
 *
 * @param string $pass Raw password
 * @param string $salt Password salt as stored in the database
 * @param string $storedPass Hashed password to match against
 */
public function verifyPassword( $pass, $salt, $storedPass ) {
	if ( empty( $pass ) || empty( $salt ) || empty( $storedPass ) ) {
		return false;
	}
	$pass = $this->password( $pass, $salt );

	if ( $storedPass === $pass ) {
		return true;
	}
	return false;
}

These are very simple and are fairly self-explanatory. You’re seeing the PHP “crypt” function being used here to create a blowfish hash with a cost of 9 (this is the work factor used to calculate the hash; the higher it is, the longer it takes to hash, thus more expensive to brute-force). The verifyPassword function does the hash again with the salt (even though bcrypt has built in salting, I prefer adding a randomly generated one of my own for more entropy) to see if it matches the stored hash.

Now the DIY version

Simply re-hashing a bunch of times isn’t usually good enough, especially if it’s just md5. I’ve lost count of how many times I’ve seen that. There are well known software packages out there that still insist on putting md5 in a loop 1000x or more, but realistically, these are fairly easy to crack these days with the proliferation of fast GPUs.

If for some reason, you still want to do your own hashing (as mentioned above, I don’t recommend it), I’ve written an alternative you can use :

/**
 * DIY hash, best to use this for something non-critical
 */
public function saltedHash( $str, $salt = NULL ) {
	if ( NULL === $salt ) {
		$salt = bin2hex( $this->IV( 30 ) );
	}
	
	for ( $i = 0; $i < 5000; $i++ ) {
		$str	= hash( 'ripemd320', $str );
		$arr	= str_split( $str, 8 );
		$j	= $i;
		
		foreach( $arr as &$a ) {
			$salt	= strrev( $salt ) . $j;
			$str	= hash( 'ripemd160', strrev( $a ) . $salt );
			$j++;
		}	
	}

	return $salt . 'x' . $str ;
}

/**
 * Verify DIY hash
 */
public function matchSaltedHash( $stored, $sent ) {
	if ( empty( $sent ) || empty( $stored ) ) {
		return false;
	}
	
	$pkg = explode( 'x', $stored );
	if ( 1 <= count( $pkg ) ) {
		return false;
	}
	
	if( $pkg[1] === $this->saltedHash( $sent, $pkg[0] ) ) {
		return true;
	}
	
	return false;
}

/**
 * @returns mixed Binary IV
 */
public function IV( $size ) {
	return mcrypt_create_iv( $size, MCRYPT_DEV_URANDOM );
}

This function is expensive enough to be not a simple re-iteration that can create hashes which are easily cracked on a modern GPU in short order. Because the hash is also salted, it should be a bit more difficult to simply run reiterations. It won’t make it uncrackable, but certainly more secure than simply running md5 a thousand times.

If you’re interested in a class for cryptographic purposes (encryption/decryption, password hashing/generation etc…) I’ve created a complete example. Note: I’m using a defined constant called SALT_KEY, which is basically an application specific random string. You can create your one of your own via the WordPress salt generator (do NOT share the same salt among multiple applications).

I’ve already published much of this code previously, but this version includes improvements.

<?php

/**
 * Encryption/Decryption, Encoding/Decoding and password related functions.
 * 
 * The license below was inherited from the project this originally went into.
 * If you need a different license for some reason, just email me.
 * 
 * @author Eksith Rodrigo <reksith at gmail.com>
 * @license http://opensource.org/licenses/ISC ISC License
 */

class Crypto {
	
	/**#@+
	 * Instance getter, constructor and destructor
	 */
	
	private static $_instance;
	public static function getInstance() {
		if ( !self::$_instance instanceof self ) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	
	private function __construct() {}
	
	/**#@-*/
	
	
	
	/**#@+
	 * Password related functions
	 */
	
	
	/**
	 * Hash password with blowfish + salt
	 *
	 * @param string $pass Raw password
	 * @param string $decodedSalt Stored salt after being decoded or freshly generated
	 */
	public function password( $pass, $decodedSalt ) {
		// This will reject NULL, 0, FALSE etc... but these are terrible passwords anyway
		if ( empty( $pass ) || empty( $decodedSalt ) ) {
			return false;
		}
		
		return crypt( $pass, '$2y$09$'. $decodedSalt . '$' );
	}
	
	
	/**
	 * Verify password hash against the stored value
	 * 
	 * @param string $pass Raw password
	 * @param string $encodedSalt Password salt as stored in the database
	 * @param string $storedPass Hashed password to match against 
	 */
	public function verifyPassword( $pass, $encodedSalt, $storedPass ) {
		if ( empty( $pass ) || empty( $encodedSalt ) || empty( $storedPass ) ) {
			return false;
		}
		
		$salt = $this->decode( $encodedSalt, SALT_KEY );
		$pass = $this->password( $pass, $salt );
		
		if ( $storedPass === $pass ) {
			return true;
		}
		return false;
	}
	
	
	/**
	 * Hashes the passed password and creates a matching salt (also encoded)
	 * This function does NOT create a new password.
	 * 
	 * @param string $pass User provided password
	 * @param string $salt Generated salt (encoded using the password)
	 */
	public function pairPasswordSalt( &$pass, &$salt ) {
		if ( empty( $pass ) ) {
			return false;
		}
		
		$salt = bin2hex( $this->IV( 30 ) ); 
		$pass = $this->password( $pass, $salt );
		
		$salt = $this->encode( $salt, SALT_KEY );
		
		return true;
	}
	
	
	/**
	 * Random password generator
	 * 
	 * @param int $len Password length
	 * @return string Random password with commonly confused characters removed
	 */
	public function randomPassword( $len ) {
		// Extra padding to accomodate removed chars
		$r = $this->randomStr( $len + 15 );
		
		// Remove confusing chars
		$r = str_replace( array( '1', 'i', 'l', '0', 'o', '5', 's' ), '', $r );
		
		return substr( $r, 0, $len );
	}
	
	/**#@-*/

	
	
	/**#@+
	 * Random number/string generators
	 */
	
	/**
	 * Generates a random string of a given length (or random between 5 and 10 if length is empty)
	 * 
	 * @param int $len Random string length. If empty, will revert to a number between 10 - 20
	 * @return string Alphanumeric string
	 */
	public function randomStr( $len = 0 ) {
		if ( empty( $len ) ) {
			$len = $this->_rnd( 10, 20 );
		}
		
		$r = base64_encode( $this->IV( 30 ) );
		$r = str_replace( array( '+', '/', '-', '_', '=' ), '', $r );
		if ( strlen( $r ) > $len ) {
			return substr( $r, 0, $len );
		}
		return $r;
	}
	
	
	/**
	 * Generates a random number
	 * 
	 * @param int $min Minimum number size. Defaults to 1
	 * @param int $max Maximum number size. Defaults to maximum allowed in 32 bit PHP
	 */
	public function randomInt( $min = 1, $max = 0x7FFFFFFF ) {
		$d = $max - $min;
		if ( $d < 0 || $d > 0x7FFFFFFF ) {
			$d = 1;
		}
		
		$r = $this->IV( 4 );
		$p = unpack( "Nint", $r );
		$f = ( float ) ( $p['int'] & 0x7FFFFFFF ) / 2147483647.0;
		
		return  round( $f * $d ) + $min;
	}
	
	/**#@-*/
	
	
	
	/**
	 * Encrypt and serialize data with a given key
	 * 
	 * @param mixed $data Data to encrypt (assumes strings are UTF-8)
	 * @param string $key Encryption key
	 * @return string Serialized and encrypted
	 */
	public function cryptSerialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = json_encode( $data );
			return $this->encryption( $data, $key, 'encrypt' );
		}
	}
	
	
	/**
	 * Deserialize and decrypt data
	 * This is safer than PHP's 'deserialize' especially with cookie data
	 *
	 * @param string $data Data to decrypt (this should be completely unaltered)
	 * @param string $key Decryption key
	 * @return mixed Orignally serialized object
	 */
	public function cryptDeserialize( $data, $key ) {
		if ( empty( $data ) ) {
			return '';
		} else {
			$data = $this->encryption( $data, $key, 'decrypt' );
			if ( empty( $data ) ) {
				return '';
			} else {
				try {
					return json_decode( $data );
				} catch ( Exception $e ) {
					return '';
				}
			}
		}
	}
	

	/**
	 * Encode ( so called because this is only ECB ) in Rijndael 256 with Base64 encoding
	 *
	 * @param string $data The content to be encoded
	 * @param string $salt Required nonce
	 * @return string Encoded and optionally base64 encoded string
	 */
	public function encode( $data, $salt ) {
		$enc = mcrypt_encrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		return base64_encode( $enc );
	}
	
	
	/**
	 * Decode as encoded above 
	 *
	 * @param string $data The content to be decoded
	 * @param string $salt Required matching nonce to the encrypted data
	 * @return string Decoded content
	 */
	public function decode( $data, $salt ) {
		$data = base64_decode( $data );
		$dec = mcrypt_decrypt( MCRYPT_RIJNDAEL_256, $salt, $data, MCRYPT_MODE_ECB, '' );
		
		// Padding is automatic, but unpad is not
		return $this->_unpad( $dec, 32 );
	}
	

	/**
	 * Generates a random IV for encryption and password/string generation
	 * 
	 * @returns mixed Binary IV
	 */
	public function IV( $size ) {
		return mcrypt_create_iv( $size, MCRYPT_DEV_URANDOM );
	}
	
	
	/**
	 * Decode as encoded above in Rijndael 256 with optional Base64 decoding 
	 *
	 * @param string $data The content to be decoded.
	 * @return string Decoded content
	 */
	public function encryption( $str, $key, $mode = 'encrypt', $time = null ) {
		// Open Rijndael-256 in CBC mode
		$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
		
		// Find the IV size for the mcrypt module
		$size = mcrypt_enc_get_iv_size( $td );
		
		// Block sizes are needed to calculate the padding ( further down )
		$bsize = mcrypt_enc_get_block_size( $td );
		
		// Key size
		$ksize = mcrypt_enc_get_key_size( $td );
		
		// Hashed key ( for consistency )
		$hkey = hash( 'ripemd160', $key );
		
		// Hash the key for consistency and use only the key size needed from the front
		$key = substr( $hkey, 0, $ksize );
		
		// Salt for encoding/decoding is extracted from the back of the key hashed again
		$salt = substr(  hash( 'ripemd160', $hkey ), ( -1 * $ksize ) );
		
		if ( $mode == 'encrypt' ) {
			/// We're encrypting. Create a new initialization vector
			$iv = $this->IV( $size );
		} else {
			// Unwrap the combined IV and data packet and extract the IV from the front (using IV size)
			$str = $this->decode( $str, $salt );
			$iv = mb_substr( $str, 0, $size );
			
			// Isolate the data by removing the IV altogether and decode in preparation for decryption
			$str = mb_substr( $str, mb_strlen( $iv ) );
			$str = base64_decode( $str );
		}
		
		// Initialize mcrypt
		mcrypt_generic_init( $td, $key, $iv );
		if ( $mode == 'encrypt' ) {
			// Prepare string by padding to match block size and encrypt
			$str = $this->_pad( $str, $bsize );
			$enc = mcrypt_generic( $td, $str );
			
			// Add the IV to the front and Encode
			$out = $this->encode( $iv . base64_encode( $enc ), $salt );
		} else {
			// Decrypt the data ( we removed the IV and decoded above ) and remove padding
			$str = mdecrypt_generic( $td, $str );
			$out = $this->_unpad( $str, $bsize );
		}
		
		// Clean up
		mcrypt_generic_deinit( $td );
		mcrypt_module_close( $td );
		
		// Return encrypted/decrypted string
		return $out;
	}
	
	
	/**#@+
	 * Helpers
	 */
		
	/**
	 * DIY hash, best to use this for something non-critical
	 */
	public function saltedHash( $str, $salt = NULL ) {
		if ( NULL === $salt ) {
			$salt = bin2hex( $this->IV( 30 ) );
		}
		
		for ( $i = 0; $i < 5000; $i++ ) {
			$str	= hash( 'ripemd320', $str );
			$arr	= str_split( $str, 8 );
			$j	= $i;
			
			foreach( $arr as &$a ) {
				$salt	= strrev( $salt ) . $j;
				$str	= hash( 'ripemd160', strrev( $a ) . $salt );
				$j++;
			}
			
		}
		
		return $salt . 'x' . $str ;
	}
	
	/**
	 * Verify DIY hash
	 */
	public function matchSaltedHash( $stored, $sent ) {
		if ( empty( $sent ) || empty( $stored ) ) {
			return false;
		}
		
		$pkg = explode( 'x', $stored );
		if ( 1 <= count( $pkg ) ) {
			return false;
		}
		
		if( $pkg[1] === $this->saltedHash( $sent, $pkg[0] ) ) {
			return true;
		}
		
		return false;
	}
	
	
	/**
	 * Workaround for mt_rand abnormalities
	 */
	public function _rnd( $min, $max ) {
		$r = 0;
		if ( $min > $max ) {
			$min ^= $max;
			$max ^= $min;
			$min ^= $max;
		}
		while( 0 === $r || $r < $min || $r > $max ) {
			$r = mt_rand( $min, $max );
		}
		
		return $r;
	}
	
	
	/**
	 * Pad data to encryption block size.
	 * Michael Corleone says hello.
	 */
	private function _pad( $str, $bsize ) {
		// Find the pad size for this block size and string length
		$pad = $bsize - ( mb_strlen( $str ) % $bsize );
		
		// Repeat the equivalent character up to the pad size
		$str .= str_repeat( chr( $pad ), $pad );
		
		return $str;
	}
	
	
	/**
	 * Remove extra padding added during encryption.
	 * This is a bit of a hack, so if you have improvements, please add them [ and let me know :) ].
	 * Thanks!
	 */
	private function _unpad( $str, $bsize ) {
		$len = mb_strlen( $str );
		
		// Find the pad character ( last one )
		$pad = ord( $str[$len - 1] );
		
		// If padding would have been applied to the string...
		if ($pad && $pad < $bsize) {
			// ...find the pad
			$pm = preg_match( '/' . chr( $pad ) . '{' . $pad . '}$/', $str );
			
			// Pad found, strip it.
			if( $pm ) {
				return mb_substr($str, 0, $len - $pad);
			}
		}
		return $str;
	}
		
	/**#@-*/
}

Cryptographically secure One-time Pads

It’s the end of Christmas day… And I’ve got a splitting headache (because I don’t drink and had to watch everything). Luckily I’m not covered in someone else’s puke or urine, which is always nice.  No one drove into any trees (as far as I know), lost their pants or an eye.

Super…

Just finished checking my email and it seems a quite a few people were interested in my last post on the one-time pad and one frequent question was how to make it secure enough for truly private communication. First off, the JavaScript version is out if you want true security. Second, it’s best to implement something in a reusable class that can be used perhaps in a web application or client-side app. I chose an MVC web app in C# for this demonstration because my brain won’t let me stay up for much longer.

To make a large enough one-time pad that is both cryptographically secure and still computationally practical, we have to balance between how strong the algorithm is and how often it is iterated. In my original example, I opted to shuffle the character pool using the Fisher-Yates algorithm before a character is picked and each time, the pick was also random from the shuffled pool.

Considering JavaScript’s inherent weakness in that it must rely on the browser’s scripting engine for the psudorandom number, these were necessary improvements even with the added overhead. However, since modern browers do have optimized JS engines, this wasn’t a huge concren.

The problem is when we move toward much stronger cryptographic functions where computation starts to become non-trivial.

In the case of the RNGCryptoServiceProvider, the class provides a much stronger random number than the simple Random function. The output is spectrally white and is better suited for generating keys. The main point here is that it is cryptographically secure. I.E. It’s random enough to be used for proper encryption while still being a psudorandom number generator. The down side is that it is more computationally intensive than just plain Random.

The solution is to not shuffle the pool between character picks, but randomize between “segments”; the separated 6-8 character “words” in the pad. This strikes a good balance between randomness and speed.

Then, the issue comes down to how the pad is generated and presented to the user. Conventionally, this was in the form of plain text or as a downloadable file, however one of the requests I received was to make something that can do both. If the one-time pad can be saved as an image file, it can be sent via encrypted email to the recipient. The risk is that browsers store images and the like and the browser cache must be emptied after each generation. If a printer is used, it must also be cleared of its cache because some printers save a history of printed documents.

The following is a quick class that has both text and image generation. The GetImg function can be used to turn any sort of text into an image byte array, not just pad text.

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

using System;
using System.Security;
using System.Security.Cryptography;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Text;

namespace OneTimePad
{
	public class PadModel
	{
		// Render settings
		FontFamily renderFont = FontFamily.GenericMonospace;
		FontStyle renderStyle = FontStyle.Bold;
		GraphicsUnit renderUnit = GraphicsUnit.Pixel;
		int renderSize = 10;

		// Creates formatted pages of keys
		public string RenderPad(int s, int l, string chars)
		{
			// Result
			StringBuilder sb = new StringBuilder();

			// First page
			int p = 1;

			for (int i = 0; i < l; i++)
			{
				// First page number
				if (p == 1 && i == 0)
					sb.Append("1.\n\n");

				// Generate segment
				sb.Append(GenerateRandomString(s, chars));

				// Page, number and segment separartion
				if (i % 63 == 62)
				{
					if (i + 1 < l)
					{
						p++;
						sb.Append("\n\n\n");
						sb.Append(p);
						sb.Append(".\n\n");
					}
				}
				else if (i % 7 == 6) // Line separation
				{
					sb.Append("\n");
				}
				else // Segment separation
				{
					sb.Append("   ");
				}
			}

			return sb.ToString();
		}

		// Generates a random string of given length
		public static string GenerateRandomString(int len, string range)
		{
			Byte[] _bytes = new Byte[len];
			char[] _chars = new char[len];

			// Shuffle the range first
			range = Shuffle(range);

			using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
			{
				rng.GetBytes(_bytes);

				for (int i = 0; i < len; i++)
					_chars[i] = range[(int)_bytes[i] % range.Length];
			}

			return new string(_chars);
		}

		// Implements the Fisher-Yates algorithm to shuffle the range
		public static string Shuffle(string range)
		{
			char[] _chars = range.ToCharArray();
			int len = _chars.Length;
			Byte[] _bytes = new Byte[len];

			using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
			{
				for (int i = len - 1; i > 1; i--)
				{
					// New set of random bytes
					rng.GetBytes(_bytes);

					int r = (int)_bytes[i] % len;
					char c = _chars[i];
					_chars[i] = _chars[r]; // Swap
					_chars[r] = c;
				}
			}

			return new string(_chars);
		}

		// Generates a jpeg of given text
		public byte[] GetImg(string txt)
		{
			// Blank image
			Bitmap bmp = new Bitmap(1, 1);
			Graphics gfx = Graphics.FromImage(bmp);

			// Font settings
			Font fnt = new Font(renderFont, renderSize, renderStyle, renderUnit);

			// Image dimensions
			int w = (int)gfx.MeasureString(txt, fnt).Width;
			int h = (int)gfx.MeasureString(txt, fnt).Height;

			// New image to text size
			bmp = new Bitmap(bmp, new Size(w, h));

			gfx = Graphics.FromImage(bmp);

			// Defaults
			gfx.Clear(Color.White);
			gfx.SmoothingMode = SmoothingMode.Default;

			gfx.TextRenderingHint =
				System.Drawing.Text.TextRenderingHint.AntiAlias;

			gfx.DrawString(txt, fnt, new SolidBrush(Color.Black), 0, 0);

			// Cleanup
			gfx.Flush();

			MemoryStream ms = new MemoryStream();
			bmp.Save(ms, ImageFormat.Png);
			return ms.ToArray();
		}
	}
}

 

To use this in an MVC application, for example, I would use the following ActionResult.

public ActionResult Index(FormCollection collection)
{
	// Generate
	string chars = "2346789ABCDEFGHKLMNPQRTWXYZ";

	// Default values
	int ds = 8, dl = 882;

	// Store (s = segment size, l = number of segments)
	int s = 0, l = 0;

	// Assign values
	if (Int32.TryParse(collection["s"], out s) || Int32.TryParse(collection["l"], out l))
	{
		// Set defaults to practical limits
		s = (s > 0 && s <= 20) ? s : ds;
		l = (l > 0 && l <= 1000) ? l : dl;
	}
	else
	{
		// Set defaults
		s = ds;
		l = dl;
	}

	PadModel model = new PadModel();
	string txt = model.RenderPad(s, l, chars);
	string img = Convert.ToBase64String(model.GetImg(txt));

	ViewData["s"] = s; // Segment size to be shown
	ViewData["l"] = l; // Total segment count
	ViewData["pad"] = txt; // Plain text version of the pad

	// Instead of sending binary data, I opted to use a base64 encoded image instead
	// since most modern browsers support it anyway
	ViewData["img"] = string.Format("data:image/png;base64,{0}", img);

	return View();
}

 

And in the View page, we can display both the plain text version and the image version of the pad using the following two lines

<img src="<%= ViewData["img"] %>" alt="PadImage" />
<pre><%= ViewData["Pad"] %></pre>

 

As an added benefit of sending a base64 encoded image instead of a binary file is that the base64 text itself can be sent via encrypted email that can be reconstituted by the recipient. This added layer of security is also handy if the user intends to hide the pad in its entirety in another data file.

The image version should produce something like this…

An image based one time pad that can be printed

 

I generated that pad just a little while ago and printed it out on my laser printer. Of course the quality is rubbish because my printer is low-res and only meant for office documents, but with a proper high resolution inkjet, we can get much finer details.

 

One-time pad close-up

 

Because the sheets are numbered, the previous message can contain details on which sheet to use for the next communication. Ideally, these pages should be cut into individual sheets and destroyed after each use.

Hope this was helpful.

Now I’m off to hit the hay!

An Encrypted Christmas Greeting

A little while ago, I wrote a simple one-time pad utility in JavaScript that can be used to send encrypted messages with relative security. One-time-pad is technically cryptographically secure, however it’s only as secure as how it’s used and the strength of the random number generator used to create the pad. My script must rely on the strength of the psudo random number generator of the browser scripting engine, so security is relatively weak and this must be taken into consideration before using it for anything important.

I’ve been getting some emails asking about using the utility and I think there was some added confusion since my version uses numbers as well. We’ll here’s a little tutorial on how to use a one-time pad…

Encrypting the message

Take the following simple message for example, and take the corresponding number from the values list on the one-time-pad.

 

M E R R Y C H R I S T M A S
13 05 18 18 25 03 08 18 09 19 20 13 01 19

 

Now, using the one-time pad, I got the following set of random characters and their corresonding values.

 

D 9 D F D B L M L W W 8 8 A
04 35 04 06 04 02 12 13 12 23 23 34 34 01

 

If I take the corresponding value of each character from both lists and add them together, I can get the new ciphertext that can be sent securely to the recipient. Obviously we can’t use the numbers in green because our list of available characters only go up to 36, however, we can subtract 36 from these numbers (mod 36) so it will cycle back to the available pool.

 

13 05 18 18 25 03 08 18 09 19 20 13 01 19 +
04 35 04 06 04 02 12 13 12 23 23 34 34 01
17 40 22 24 29 05 20 31 21 42 43 47 35 20 =
17 04 22 24 29 05 20 31 21 06 07 11 35 20 mod 36

 

This new set of numbers can be turned into the ciphertext using that same values list.

 

17 04 22 24 29 05 20 31 21 06 07 11 35 20
Q D V X 3 E T 5 U F G K 9 T

Decrypting the message

So, our recipient got the encrypted ciphertext, QDVX3ET5UFGK9T, and now he/she must take these charaters and turn them into a list of values in order to decrypt it.

 

Q D V X 3 E T 5 U F G K 9 T
17 04 22 24 29 05 20 31 21 06 07 11 35 20

 

Then, using the same one-time pad key values (since they’ll both be using the same sheet) the recipient will get the random characters and their associated values. Same as above.

 

D 9 D F D B L M L W W 8 8 A
04 35 04 06 04 02 12 13 12 23 23 34 34 01

 

The recipient will then subtract the one-time pad values from the encrypted message text. Basically the reverse of what the sender did. Again, this may result in some numbers going out of bounds of the available values list and become negative (marked in red), which we can fix by adding 36, again the opposite of what we did above, to bring it back to the available character pool.

 

17 04 22 24 29 05 20 31 21 06 07 11 35 20
04 35 04 06 04 02 12 13 12 23 23 34 34 01
13 31 18 18 25 03 08 18 09 17 16 23 01 19 =
13 05 18 18 25 03 08 18 09 19 20 13 01 19 mod 36

 

And when we turn those numbers back into the original text using the values list, we get :

 

13 05 18 18 25 03 08 18 09 19 20 13 01 19
M E R R Y C H R I S T M A S

 

… And a very Happy New Year!

Ultra-secure passwords part II

This is mostly a followup to my original post on easily generating passwords from a mnemonic. It was prompted by a comment by Francesco Sullo advocating against the method, saying “If the attacker is a cryptoanalyst and he catch two of your generated “super secure” passwords (for example, because you login into his websites) he can easily discover your method in minutes.

The premise is false, and here’s why…

For an attacker to decipher the password, I either have to be using commonly known phrases or sentences from well known books (which I don’t) or the attacker needs to scan every book in print and out of print, which is quite unlikely. Mind you the text can even be a phrase, as demonstrated in my reply to him, which no attacker would ever hope of guessing unless it was made public at some point.

As a side note, I created a quick little utility that does the hard part. There’s ample room for improvement for sure, but it gets the point across that book cipher cryptography is only weak when the source is known.