PHP Crypto

I often have to work on projects that use passwords so poorly, or use encryption so haphazardly, that I just throw my hands up and just rewrite the whole damn thing. This is a replacement for just such a damn thing. And while, I don’t expect to be a drop-in replacement for most projects, I figure it gives the basics to what most people want. If you’re starting out with a new application, maybe this will help you.

There are some things done here that may be overkill (some would say ”underkill”?) but I’ll leave it up to your discretion. If you find any errors/corrections/improvements, please let me know.

<?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 {
	
	/**
	 * Application specific random string to encode salts
	 * IMPORTANT:This should be changed in your own deployments (and never repeated)
	 * It will also be easier to move this to a defined variable
	 */
	private $saltKey = 'i^-ZsV[+vCHl)3D?-S7GiLSK~iv=qzQO%k`x%0?:Lps-[+88M//J+z- >+KKY``*';
	
	
	/**#@+
	 * Instance getter, constructor
	 */
	
	private static $instance;
	static function GetInstance() {
		if ( !isset( $instance ) ) {
			self::$instance = new Crypto();
		}
		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, $this->saltKey );
		$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 ) ); // Boyz II Men
		$pass = $this->Password( $pass, $salt );
		
		$salt = $this->Encode( $salt, $this->saltKey );
		
		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 ) {
		if ( empty( $len ) ) {
			$len = mt_rand( 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->Crypt( $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->Crypt( $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 Decrypted content
	 */
	public function Crypt( $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
	 */
	
	
	/**
	 * 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;
	}
		
	/**#@-*/
}

 

The CryptDeserialize and CryptSerialize functions were originally meant for storing encrypted cookies/sessions. You can use them for something similar, however the string values must be UTF-8 for it to work correctly (due to json_decode/encode).

7:30AM… Time for bed!

Unboxing the BlackBerry PlayBook

This is a bit of a present to myself. Normally, I eschew buying technology for technology’s sake, but over Christmas last year, I got a BlackBerry Playbook (2 actually, with one for my mom). The price was much less than an Android Nexus and since I already had an Android phone, I figured I’d give the BlackBerry a try. Yeah, I know… lots of people have done this ages ago, but I haven’t. So there!

I got the 32Gb version, mostly for the price. This doesn't have an expansion port, but I don't plan on keeping a lot of media on it. And yes, I did use a tin as a pot... so sue me!

I got the 32Gb version, mostly for the price. This doesn’t have an expansion port, but I don’t plan on keeping a lot of media on it. And yes, I did use a tin as a plant pot… so sue me!

The outer case comes out as a sleeve and underneath is a stylish BB logo box. And yes, I also used a cut up soda bottle and an old jug as pots. I'm a bachelor; I'm allowed to do stuff like that!

The outer case comes out as a sleeve and underneath is a stylish BB logo box. And yes, I used a cut up soda bottle and an old jug as pots too. I’m a bachelor; I’m allowed to do stuff like that!

The tablet inside comes in a typical "sock" like cover. If you use this for any length of time, a book sleeve is highly recommended.

The tablet inside comes in a typical “sock” like cover. If you use this for any length of time, a book sleeve is highly recommended.

Underneath is the documentation and charger.

Underneath is the documentation and charger.

Product info box has the booklet and the "Do and Don't" list in three languages. The charger box also has the USB cable and a nifty screen wipe cloth (which you will use a lot)

Product info box has the booklet and the “Do and Don’t” list in three languages. The charger box also has the USB cable and a nifty screen wipe cloth (which you will use a lot)

The startup time can be almost a minute, maybe more. I've been using it for 3 months now and it still hasn't changed

The startup time can be almost a minute, maybe more. I’ve been using it for 3 months now and it still hasn’t changed

You get to the next screen by swiping. And I can already see the fingerprints

You get to the next screen by swiping. And I can already see the fingerprints

After selecting language and region, you get to your BB ID signup. Note you get 10 tries after which, I think it can lock up. I didn't purposely lock myself out to find out ;)

After selecting language and region, you get to your BB ID signup. Note: You get 10 tries after which, I think, it can lock up. I didn’t purposely lock myself out to find out ;)

During the setup, you will be prompted to go through the "training" phase. Note: You *cannot* skip this part, which I found to be a little annoying. I guess they want to make sure people know how to use it before complaining.

During the setup, you will be prompted to go through the “training” phase. Note: You *cannot* skip this part, which I found to be a little annoying. I guess they want to make sure people know how to use it before complaining.

 I linked my BlackBerry 9930 phone to this tablet so I can use its data plan to browse. This is one feature I wished my Android had by default, but it's much smoother on the PlayBook. I get 3G speeds (that's the 9930 max) and no hiccups.

I linked my 9930 phone to this tablet using BlackBerry Bridge so I can use its data plan to browse. This is one feature I wished my Android had by default, but it’s much smoother on the PlayBook. I get 3G speeds (that’s the 9930 max) and no hiccups.

Update notification came up as soon as I connected to my WiFi. Update itself took about 10 minutes (to download and install)

Update notification came up as soon as I connected to my WiFi. You can keep your phone connected to the tablet while WiFi is on and it will use the WiFi to download. Update itself took about 10 minutes (to download and install)

During the restart after the update, I could see one of the bigger problems of the PlayBook. Now I know why they included the screen wipe cloth (see above). Even my phone isn't this bad.

During the restart after the update, I could see one of the bigger problems of the PlayBook. Now I know why they included the screen wipe cloth (see above). Even my phone isn’t this bad.

After the setup, I got to using the web browser, which I must say was very smooth. There are some issues after a second update with some YouTube videos in that I’m unable to skip ahead or rewind on certain HD videos and/or long videos, but I’m willing to chock this up to poor Flash compatibility than a fault of the PlayBook.

The apps are rather sparse, but does include a couple of games and the NFB (National Film Board) app, which is like the Canadian PBS. They have documentaries, art, culture and history related movies and I found this to be the most used app on it so far.

One thing I did note is that the popup keys while typing can get a little annoying.

Note, how there's a blue popup above the finger showing the key you just pressed. While some people might find this useful, I found it annoying.

Note, how there’s a blue popup above the finger showing the key you just pressed. While some people might find this useful, I found it annoying.

So I went into the keyboard settings (the cog icon on the top of the screen will bring up settings) and turned it off.

Swipe the black border on the top down to bring up the top border with the settings icon (along with the battery status and time), if you have another app running in the foreground.  Turn 'Keypress popup' to 'off'

Swipe the black border on the top down to bring up the top border with the settings icon (along with the battery status and time), if you have another app running in the foreground.  Turn “Keypress popup” to “off”

There was one minor issue with the PlayBook acting up a bit when I had the browser open. The browser would suddenly close for no particular reason as soon as I opened it. I solved this by going into settings and changing the mode to “Showcase” (Settings > General > Application Behavior ). I guess this prevents apps from going into hibernation when running in the background, which may eat into battery life, but so far, I haven’t seen much of a battery issue.

Which isn’t the case with the BlackBerry 9930 which eats battery life like nothing else.

So I think I like my PlayBook. It’s certainly not the biggest at just 7 inches, but I don’t see much of a problem with that considering what I’m using it for; that is browsing, email and maybe a video or two. My biggest problem so far has been that I keep forgetting it in the bathroom. I need to find a way to shorten poopie time or bring a newspaper next time instead.

Celebrating a milestone

This will be followed by a proper update, I promise. Meanwhile, I wanted to share what I think is a pretty good achievement. My OpenBSD box just reached 365 days of uptime (that’s days without being restarted, for the non-geeks). Its on its own dinky little UPS which I didn’t even think would kick in during the snow storm.

Say hi, Pudding!

Say hi, Pudding!

That’s Pudding sitting on top. There’s a story behind him (and his name), which I’ll share later ;)

I’m about to upgrade that to 5.2 so it will probably be another year until this happens again. I’m going to be hosting a couple of additional sites so PHP and Nginx need to be upgraded as well.

New Avatar

For as long as this blog has been on WordPress.com, I’ve used the ouroboros symbol as my avatar. It was a quick design I put together that mean something to me since I didn’t feel comfortable using my own face (people have fainted from fright and disgust). So there it remained for years.

Yesterday, I thought maybe I can put together an avatar. After mucking about a few online tools, I settled on FaceYourManga and created this :

It's kinda, sorta what I look like

It’s kinda, sorta what I look like

I think it looks better than my actual face.

 

Holding a pen correctly

An alarming number of literate people have no idea how to hold their favorite writing instrument. This is partly due to parents and teachers being more concerned with getting the script on paper vs. actually teaching proper holding technique. Sadly, a large number of teachers are also unaware of how to hold a pen.

How to hold your pen/pencil correctly

Your thumb, index finger and middle finger create a sort of open triangle. The index and middle finger don’t need to be closed, however the middle finger does need to cross over toward the thumb a little bit to form a “shelf”. The pencil really rests on the tip of the middle finger while the index finger and thumb prevent the pen from sliding about on this “shelf”.

Note how little effort is needed to hold the pen.

Note how little effort is now needed to hold the pen.

The "open triangle". Note the index and middle finger really does the writing, while the middle finger just sort of rides along.

The “open triangle”. Note the index and middle finger really does the writing, while the middle finger just sort of rides along below.

While the middle finger creates the "shelf" to hold the pen, the ring finger, little finger and the base of the thumb create the "pad" the hand rests on.

While the middle finger creates the “shelf” to hold the pen, the ring finger, little finger and the base of the thumb create the “pad” the hand rests on.

It’s your thumb and index finger that actually do the writing by moving in unison while the middle finger sort of tags along under both of them holding the pen up towards them.

When the index finger has this slight curve, you’re not putting too much pressure on the pen. Remember you’ve got fingers; not a pair of vise-grips so don’t treat them as such.

Your ring finger, little finger and the lower part of your thumb create the pad that lets you glide about the paper.

What’s the big deal?

This arrangement gives the greatest amount of dexterity when it comes to writing while requiring the least amount of effort. Also, I’ve had juvenile arthritis since the age of 12 so you can bet I’ll look for the least awkward and least painful way to write as long as possible if I can help it.

The longer you write, the more tired and strained your fingers will feel if you don’t hold your pen correctly. If you’ve felt tired or your fingers hurt after writing what seems like a short period of time, chances are you’re holding your pen awkwardly.

No, but really, what’s the big deal?

Everyone types these days. It’s come to a point where handwriting, when it comes to communication, is at about the same level as walking when it comes to transport. People don’t do it because they need to; they do it unless other forms are not available, not applicable or inappropriate.

This is a shame, really.

Humans are not unique when it comes to complex speech. Plenty of other creatures in this world have far more complex speech patterns in a greater range of frequencies that humans can’t even perceive let alone articulate. We aren’t unique in our concept of culture either. Dolphins are routinely known to hunt with different characteristics depending on the area of their range, even though they may be the same species. Likewise, wolves also exhibit uniqueness from pack to pack. Same species, different behavior depending on community and location. We call that “culture”.

The only real difference between us and most other animals is a writing system that lets us pass knowledge from generation to generation. Whereas other creatures are pretty much reduced to chemical secretions when they want to leave a message. In fact, it’s our writing that has allowed us to advance this far and, of course, opposable thumbs helped. Our civilization really owes its existence to writing, not just speech.

Now that you have these wonderful tools at your disposal, wouldn’t you want to know how to use them correctly?

A little background

I’m left-handed, though I usually write with my right hand (unless I’m holding a cup of coffee with my right, or in this case, my phone to take the above pictures).

It became really obvious that I’m left-handed when a few years ago I suffered almost complete numbness in my left hand due to the side-effects of a heart medication I was taking at the time. A lot of things suddenly became far more awkward than I expected because… oh right, I used to use my left hand for that.

I also recently started learning the guitar and I fear my lack of progress with the right-handed instrument may also be due to me being left-handed in addition to the shortage of free time.

When I was in kindergarten, I had an awesome teacher; probably the best I’ve ever had. In many ways she’s the reason I am who I am today and was really responsible for cultivating my curiosity and a tenacity when it comes to satiating it. That aspect has stayed with me to this day, but unfortunately, she had a not so awesome assistant.

Despite me being left-handed, the assistant, in a not so delicate way, made sure I used the right hand when writing, although my repeated switch to the left should have been a hint. The mere concept of a left-handed writer seemed to have been alien to her and, since I was 2-3 at the time, I couldn’t really mount an effective protest. As a result, I now write mostly with my right hand. This is far more common than a lot of people realise.

But she did at least show me how to hold my pencil correctly so I guess I’m grateful for that.