1: <?php
2: // Requires libmcrypt v2.4.x or higher
3:
4: /**
5: * Class QCryptographyException: If an exception occurs in the QCryptography class, we use this one to handle
6: */
7: class QCryptographyException extends QCallerException {
8: }
9:
10: /**
11: * Class QCryptography: Helps in encrypting and decrypting data
12: * It depends on the mcrypt module.
13: *
14: * While this module IS serializeable, the information it contains in serialized form is not secure and can be used
15: * to decrypt encrypted items. So, make sure you either encrypt the serialized results, or store the results in a
16: * secure location. That means, if you are using this in a form object, you must make sure your formstates are
17: * not visible to the public, or they are encrypted, since the serialized version of this object will be in the formstate.
18: *
19: * Refer: http://php.net/manual/en/book.mcrypt.php
20: */
21: class QCryptography extends QBaseClass {
22: /** @var resource Mcrypt algorithm module resource */
23: protected $objMcryptModule;
24: /** @var bool Are we going to use Base 64 encoding? */
25: protected $blnBase64;
26: /** @var string Key to be used for encryption/decryption - used for mycrypt_generic_init */
27: protected $strKey;
28: /** @var string Initialization vector for the algorithm */
29: protected $strIv; // Note that this is NOT used in ECB, the default mode.
30: /** @var string Cipher to use when creating the encryption object */
31: protected $strCipher;
32: /** @var string Mode to use when creating the encryption object */
33: protected $strMode;
34: /** @var string Random source to use when creating IVs */
35: protected $strRandomSource;
36:
37:
38: /**
39: * Default Base64 mode for any new QCryptography instances that get constructed.
40: * This is similar to MIME-based Base64 encoding/decoding, but is safe to use
41: * in URLs, POST/GET data, and any other text-based stream.
42: * Note that by setting Base64 to true, it will result in an encrypted data string
43: * that is 33% larger.
44: *
45: * @var string Base64
46: */
47: public static $Base64 = true;
48:
49: /**
50: * Default Key for any new QCryptography instances that get constructed
51: *
52: * @var string Key
53: */
54: public static $Key = "qc0Do!d3F@lT.k3Y";
55:
56: /**
57: * The Random Number Generator the library uses to generate the IV:
58: * - MCRYPT_DEV_RANDOM = /dev/random (only on *nix systems)
59: * - MCRYPT_DEV_URANDOM = /dev/urandom (only on *nix systems)
60: * - MCRYPT_RAND = the internal PHP srand() mechanism
61: * (on Windows, you *must* use MCRYPT_RAND, b/c /dev/random and /dev/urandom doesn't exist)
62: * TODO: there appears to be some /dev/random locking issues on the QCubed development
63: * environment (using Fedora Core 3 with PHP 5.0.4 and LibMcrypt 2.5.7). Because of this,
64: * we are using MCRYPT_RAND be default. Feel free to change to to /dev/*random at your own risk.
65: *
66: * @param null|string $strKey Encryption key
67: * @param null|bool $blnBase64 Are we going to use Base 64 encoded data?
68: * @param null|string $strCipher Cipher to be used (default is MCRYPT_TRIPLEDES)
69: * @param null|string $strMode Mode (default is MCRYPT_MODE_ECB)
70: * @param null|string $strRandomSource Random source (default is MCRYPT_RAND)
71: *
72: * @throws Exception
73: * @throws QCallerException
74: * @throws QCryptographyException
75: */
76: public function __construct($strKey = null, $blnBase64 = null, $strCipher = null, $strMode = null,
77: $strRandomSource = null) {
78: if (!function_exists('mcrypt_module_open')) {
79: throw new QCryptographyException("PHP cryptography components (libmcrypt module) are not installed");
80: }
81:
82: // Get the Key
83: if (is_null($strKey)) {
84: $strKey = self::$Key;
85: }
86:
87: // Get the Base64 Flag
88: try {
89: if (is_null($blnBase64)) {
90: $this->blnBase64 = QType::Cast(self::$Base64, QType::Boolean);
91: } else {
92: $this->blnBase64 = QType::Cast($blnBase64, QType::Boolean);
93: }
94: } catch (QCallerException $objExc) {
95: $objExc->IncrementOffset();
96: throw $objExc;
97: }
98:
99: // Get the Cipher
100: if (is_null($strCipher)) {
101: $this->strCipher = MCRYPT_TRIPLEDES;
102: } else {
103: $this->strCipher = $strCipher;
104: }
105:
106: // Get the Mode
107: if (is_null($strMode)) {
108: $this->strMode = MCRYPT_MODE_ECB;
109: } else {
110: $this->strMode = $strMode;
111: }
112:
113: if (is_null($strRandomSource)) {
114: $this->strRandomSource = MCRYPT_RAND;
115: } else {
116: $this->strRandomSource = $strRandomSource;
117: }
118:
119: $this->objMcryptModule = mcrypt_module_open($this->strCipher, null, $this->strMode, null);
120: if (!$this->objMcryptModule) {
121: throw new QCryptographyException('Unable to open LibMcrypt Module');
122: }
123:
124: // Determine IV Size
125: $intIvSize = mcrypt_enc_get_iv_size($this->objMcryptModule);
126:
127: // Create the IV
128: if ($intIvSize) {
129: if ($this->strRandomSource != MCRYPT_RAND) {
130: // Ignore All Warnings
131: set_error_handler('QcubedHandleError', 0);
132: $intCurrentLevel = error_reporting();
133: error_reporting(0);
134: $strIv = mcrypt_create_iv($intIvSize, $this->strRandomSource);
135: error_reporting($intCurrentLevel);
136: restore_error_handler();
137:
138: // If the RandomNumGenerator didn't work, we revert back to using MCRYPT_RAND
139: if (strlen($strIv) != $intIvSize) {
140: srand();
141: $strIv = mcrypt_create_iv($intIvSize, MCRYPT_RAND);
142: }
143: } else {
144: srand();
145: $strIv = mcrypt_create_iv($intIvSize, MCRYPT_RAND);
146: }
147: $this->strIv = $strIv;
148: }
149:
150: // Determine KeySize length
151: $intKeySize = mcrypt_enc_get_key_size($this->objMcryptModule);
152:
153: // Create the Key Based on Key Passed In
154: $this->strKey = substr(md5($strKey), 0, $intKeySize);
155: }
156:
157: /**
158: * Encrypt the data (depends on the value of class members)
159: * @param string $strData
160: *
161: * @return mixed|string
162: * @throws QCryptographyException
163: */
164: public function Encrypt($strData) {
165: // Initialize Encryption
166: $intReturnValue = mcrypt_generic_init($this->objMcryptModule, $this->strKey, $this->strIv);
167: if (($intReturnValue === false) || ($intReturnValue < 0)) {
168: throw new QCryptographyException('Incorrect Parameters used in LibMcrypt Initialization');
169: }
170: // Add Length to strData
171: $strData = strlen($strData) . '/' . $strData;
172:
173: $strEncryptedData = mcrypt_generic($this->objMcryptModule, $strData);
174: if ($this->blnBase64) {
175: $strEncryptedData = base64_encode($strEncryptedData);
176: $strEncryptedData = str_replace('+', '-', $strEncryptedData);
177: $strEncryptedData = str_replace('/', '_', $strEncryptedData);
178: $strEncryptedData = str_replace('=', '', $strEncryptedData);
179: }
180:
181:
182: // Deinitialize Encryption
183: if (!mcrypt_generic_deinit($this->objMcryptModule)) {
184: throw new QCryptographyException('Unable to deinitialize encryption buffer');
185: }
186:
187: return $strEncryptedData;
188: }
189:
190: /**
191: * Decrypt the data (depends on the value of class memebers)
192: * @param string $strEncryptedData
193: *
194: * @return string
195: * @throws QCryptographyException
196: */
197: public function Decrypt($strEncryptedData) {
198: // Initialize Encryption
199: $intReturnValue = mcrypt_generic_init($this->objMcryptModule, $this->strKey, $this->strIv);
200: if (($intReturnValue === false) || ($intReturnValue < 0)) {
201: throw new QCryptographyException('Incorrect Parameters used in LibMcrypt Initialization');
202: }
203:
204: if ($this->blnBase64) {
205: $strEncryptedData = str_replace('_', '/', $strEncryptedData);
206: $strEncryptedData = str_replace('-', '+', $strEncryptedData);
207: $strEncryptedData = base64_decode($strEncryptedData);
208: }
209: $intBlockSize = mcrypt_enc_get_block_size($this->objMcryptModule);
210: $strDecryptedData = mdecrypt_generic($this->objMcryptModule, $strEncryptedData);
211:
212: // Figure Out Length and Truncate
213: $intPosition = strpos($strDecryptedData, '/');
214: if (!$intPosition) {
215: throw new QCryptographyException('Invalid Length Header in Decrypted Data');
216: }
217: $intLength = substr($strDecryptedData, 0, $intPosition);
218: $strDecryptedData = substr($strDecryptedData, $intPosition + 1);
219: $strDecryptedData = substr($strDecryptedData, 0, $intLength);
220:
221: // Deinitialize Encryption
222: if (!mcrypt_generic_deinit($this->objMcryptModule)) {
223: throw new QCryptographyException('Unable to deinitialize encryption buffer');
224: }
225:
226: return $strDecryptedData;
227: }
228:
229: /**
230: * Encrypt a file (depends on the value of class memebers)
231: *
232: * @param string $strFile Path of the file to be encrypted
233: *
234: * @return mixed|string
235: * @throws QCallerException|QCryptographyException
236: */
237: public function EncryptFile($strFile) {
238: if (file_exists($strFile)) {
239: $strData = file_get_contents($strFile);
240:
241: return $this->Encrypt($strData);
242: } else {
243: throw new QCallerException('File does not exist: ' . $strFile);
244: }
245: }
246:
247: /**
248: * Decrypt a file (depends on the value of class memebers)
249: *
250: * @param string $strFile File to be decrypted
251: *
252: * @return string
253: * @throws QCallerException|QCryptographyException
254: */
255: public function DecryptFile($strFile) {
256: if (file_exists($strFile)) {
257: $strEncryptedData = file_get_contents($strFile);
258:
259: return $this->Decrypt($strEncryptedData);
260: } else {
261: throw new QCallerException('File does not exist: ' . $strFile);
262: }
263: }
264:
265: /**
266: * Closes the mcrypt module
267: * (Some methods just want to watch the world burn!)
268: */
269: public function __destruct() {
270: if ($this->objMcryptModule) {
271: // Ignore All Warnings
272: set_error_handler('QcubedHandleError', 0);
273: $intCurrentLevel = error_reporting();
274: error_reporting(0);
275: mcrypt_module_close($this->objMcryptModule);
276: error_reporting($intCurrentLevel);
277: restore_error_handler();
278: }
279: }
280:
281: public function __sleep() {
282: return array_diff(array_keys(get_object_vars($this)), array('objMcryptModule')); // can't serialize the module, must recreate
283: }
284:
285: public function __wakeup() {
286: $this->objMcryptModule = mcrypt_module_open($this->strCipher, null, $this->strMode, null);
287: }
288:
289: }