]> git.mxchange.org Git - friendica.git/blob - library/defuse/php-encryption-1.2.1/Crypto.php
Update INSTALL.txt
[friendica.git] / library / defuse / php-encryption-1.2.1 / Crypto.php
1 <?php
2
3 /*
4  * PHP Encryption Library
5  * Copyright (c) 2014, Taylor Hornby
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice, 
12  * this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /*
32  * Web: https://defuse.ca/secure-php-encryption.htm
33  * GitHub: https://github.com/defuse/php-encryption 
34  *
35  * WARNING: This encryption library is not a silver bullet. It only provides
36  * symmetric encryption given a uniformly random key. This means you MUST NOT
37  * use an ASCII string like a password as the key parameter, it MUST be
38  * a uniformly random key generated by CreateNewRandomKey(). If you want to
39  * encrypt something with a password, apply a password key derivation function
40  * like PBKDF2 or scrypt with a random salt to generate a key.
41  *
42  * WARNING: Error handling is very important, especially for crypto code! 
43  *
44  * How to use this code:
45  *
46  *     Generating a Key
47  *     ----------------
48  *       try {
49  *           $key = self::CreateNewRandomKey();
50  *           // WARNING: Do NOT encode $key with bin2hex() or base64_encode(),
51  *           // they may leak the key to the attacker through side channels.
52  *       } catch (CryptoTestFailedException $ex) {
53  *           die('Cannot safely create a key');
54  *       } catch (CannotPerformOperationException $ex) {
55  *           die('Cannot safely create a key');
56  *       }
57  *
58  *     Encrypting a Message
59  *     --------------------
60  *       $message = "ATTACK AT DAWN";
61  *       try {
62  *           $ciphertext = self::Encrypt($message, $key);
63  *       } catch (CryptoTestFailedException $ex) {
64  *           die('Cannot safely perform encryption');
65  *       } catch (CannotPerformOperationException $ex) {
66  *           die('Cannot safely perform decryption');
67  *       }
68  *
69  *     Decrypting a Message
70  *     --------------------
71  *       try {
72  *           $decrypted = self::Decrypt($ciphertext, $key);
73  *       } catch (InvalidCiphertextException $ex) { // VERY IMPORTANT
74  *           // Either:
75  *           //   1. The ciphertext was modified by the attacker,
76  *           //   2. The key is wrong, or
77  *           //   3. $ciphertext is not a valid ciphertext or was corrupted.
78  *           // Assume the worst.
79  *           die('DANGER! DANGER! The ciphertext has been tampered with!');
80  *       } catch (CryptoTestFailedException $ex) {
81  *           die('Cannot safely perform encryption');
82  *       } catch (CannotPerformOperationException $ex) {
83  *           die('Cannot safely perform decryption');
84  *       }
85  */
86
87 /* 
88  * Raised by Decrypt() when one of the following conditions are met:
89  *  - The key is wrong.
90  *  - The ciphertext is invalid or not in the correct format.
91  *  - The attacker modified the ciphertext.
92  */
93 class InvalidCiphertextException extends Exception {}
94 /* If you see these, it means it is NOT SAFE to do encryption on your system. */
95 class CannotPerformOperationException extends Exception {}
96 class CryptoTestFailedException extends Exception {}
97
98 final class Crypto
99 {
100     // Ciphertext format: [____HMAC____][____IV____][____CIPHERTEXT____].
101
102     /* DO NOT CHANGE THESE CONSTANTS! 
103      *
104      * We spent *weeks* testing this code, making sure it is as perfect and
105      * correct as possible. Are you going to do the same after making your
106      * changes? Probably not. Besides, any change to these constants will break
107      * the runtime tests, which are extremely important for your security.
108      * You're literally millions of times more likely to screw up your own
109      * security by changing something here than you are to fall victim to an
110      * 128-bit key brute-force attack. You're also breaking your own
111      * compatibility with future updates to this library, so you'll be left
112      * vulnerable if we ever find a security bug and release a fix.
113      *
114      * So, PLEASE, do not change these constants.
115      */
116     const CIPHER = 'aes-128';
117     const KEY_BYTE_SIZE = 16;
118     const CIPHER_MODE = 'cbc';
119     const HASH_FUNCTION = 'sha256';
120     const MAC_BYTE_SIZE = 32;
121     const ENCRYPTION_INFO = 'DefusePHP|KeyForEncryption';
122     const AUTHENTICATION_INFO = 'DefusePHP|KeyForAuthentication';
123
124     /*
125      * Use this to generate a random encryption key.
126      */
127     public static function CreateNewRandomKey()
128     {
129         self::RuntimeTest();
130         return self::SecureRandom(self::KEY_BYTE_SIZE);
131     }
132
133     /*
134      * Encrypts a message.
135      * $plaintext is the message to encrypt.
136      * $key is the encryption key, a value generated by CreateNewRandomKey().
137      * You MUST catch exceptions thrown by this function. See docs above.
138      */
139     public static function Encrypt($plaintext, $key)
140     {
141         self::RuntimeTest();
142
143         if (self::our_strlen($key) !== self::KEY_BYTE_SIZE)
144         {
145             throw new CannotPerformOperationException("Bad key.");
146         }
147
148         $method = self::CIPHER.'-'.self::CIPHER_MODE;
149         
150         self::EnsureFunctionExists('openssl_get_cipher_methods');
151         if (in_array($method, openssl_get_cipher_methods()) === FALSE) {
152             throw new CannotPerformOperationException("Cipher method not supported.");
153         }
154         
155         // Generate a sub-key for encryption.
156         $keysize = self::KEY_BYTE_SIZE;
157         $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO);
158
159         // Generate a random initialization vector.
160         self::EnsureFunctionExists("openssl_cipher_iv_length");
161         $ivsize = openssl_cipher_iv_length($method);
162         if ($ivsize === FALSE || $ivsize <= 0) {
163             throw new CannotPerformOperationException();
164         }
165         $iv = self::SecureRandom($ivsize);
166
167         $ciphertext = $iv . self::PlainEncrypt($plaintext, $ekey, $iv);
168
169         // Generate a sub-key for authentication and apply the HMAC.
170         $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO);
171         $auth = hash_hmac(self::HASH_FUNCTION, $ciphertext, $akey, true);
172         $ciphertext = $auth . $ciphertext;
173
174         return $ciphertext;
175     }
176
177     /*
178      * Decrypts a ciphertext.
179      * $ciphertext is the ciphertext to decrypt.
180      * $key is the key that the ciphertext was encrypted with.
181      * You MUST catch exceptions thrown by this function. See docs above.
182      */
183     public static function Decrypt($ciphertext, $key)
184     {
185         self::RuntimeTest();
186         
187         $method = self::CIPHER.'-'.self::CIPHER_MODE;
188         
189         self::EnsureFunctionExists('openssl_get_cipher_methods');
190         if (in_array($method, openssl_get_cipher_methods()) === FALSE) {
191             throw new CannotPerformOperationException("Cipher method not supported.");
192         }
193
194         // Extract the HMAC from the front of the ciphertext.
195         if (self::our_strlen($ciphertext) <= self::MAC_BYTE_SIZE) {
196             throw new InvalidCiphertextException();
197         }
198         $hmac = self::our_substr($ciphertext, 0, self::MAC_BYTE_SIZE);
199         if ($hmac === FALSE) {
200             throw new CannotPerformOperationException();
201         }
202         $ciphertext = self::our_substr($ciphertext, self::MAC_BYTE_SIZE);
203         if ($ciphertext === FALSE) {
204             throw new CannotPerformOperationException();
205         }
206
207         // Regenerate the same authentication sub-key.
208         $akey = self::HKDF(self::HASH_FUNCTION, $key, self::KEY_BYTE_SIZE, self::AUTHENTICATION_INFO);
209
210         if (self::VerifyHMAC($hmac, $ciphertext, $akey))
211         {
212             // Regenerate the same encryption sub-key.
213             $keysize = self::KEY_BYTE_SIZE;
214             $ekey = self::HKDF(self::HASH_FUNCTION, $key, $keysize, self::ENCRYPTION_INFO);
215
216             // Extract the initialization vector from the ciphertext.
217             self::EnsureFunctionExists("openssl_cipher_iv_length");
218             $ivsize = openssl_cipher_iv_length($method);
219             if ($ivsize === FALSE || $ivsize <= 0) {
220                 throw new CannotPerformOperationException();
221             }
222             if (self::our_strlen($ciphertext) <= $ivsize) {
223                 throw new InvalidCiphertextException();
224             }
225             $iv = self::our_substr($ciphertext, 0, $ivsize);
226             if ($iv === FALSE) {
227                 throw new CannotPerformOperationException();
228             }
229             $ciphertext = self::our_substr($ciphertext, $ivsize);
230             if ($ciphertext === FALSE) {
231                 throw new CannotPerformOperationException();
232             }
233             
234             $plaintext = self::PlainDecrypt($ciphertext, $ekey, $iv);
235
236             return $plaintext;
237         }
238         else
239         {
240             /*
241              * We throw an exception instead of returning FALSE because we want
242              * a script that doesn't handle this condition to CRASH, instead
243              * of thinking the ciphertext decrypted to the value FALSE.
244              */
245              throw new InvalidCiphertextException();
246         }
247     }
248
249     /*
250      * Runs tests.
251      * Raises CannotPerformOperationException or CryptoTestFailedException if
252      * one of the tests fail. If any tests fails, your system is not capable of
253      * performing encryption, so make sure you fail safe in that case.
254      */
255     public static function RuntimeTest()
256     {
257         // 0: Tests haven't been run yet.
258         // 1: Tests have passed.
259         // 2: Tests are running right now.
260         // 3: Tests have failed.
261         static $test_state = 0;
262
263         if ($test_state === 1 || $test_state === 2) {
264             return;
265         }
266
267         try {
268             $test_state = 2;
269             self::AESTestVector();
270             self::HMACTestVector();
271             self::HKDFTestVector();
272
273             self::TestEncryptDecrypt();
274             if (self::our_strlen(self::CreateNewRandomKey()) != self::KEY_BYTE_SIZE) {
275                 throw new CryptoTestFailedException();
276             }
277
278             if (self::ENCRYPTION_INFO == self::AUTHENTICATION_INFO) {
279                 throw new CryptoTestFailedException();
280             }
281         } catch (CryptoTestFailedException $ex) {
282             // Do this, otherwise it will stay in the "tests are running" state.
283             $test_state = 3;
284             throw $ex;
285         }
286
287         // Change this to '0' make the tests always re-run (for benchmarking).
288         $test_state = 1;
289     }
290
291     /*
292      * Never call this method directly!
293      */
294     private static function PlainEncrypt($plaintext, $key, $iv)
295     {
296         
297         $method = self::CIPHER.'-'.self::CIPHER_MODE;
298         
299         self::EnsureConstantExists("OPENSSL_RAW_DATA");
300         self::EnsureFunctionExists("openssl_encrypt");
301         $ciphertext = openssl_encrypt(
302             $plaintext,
303             $method,
304             $key,
305             OPENSSL_RAW_DATA,
306             $iv
307         );
308         
309         if ($ciphertext === false) {
310             throw new CannotPerformOperationException();
311         }
312
313         return $ciphertext;
314     }
315
316     /*
317      * Never call this method directly!
318      */
319     private static function PlainDecrypt($ciphertext, $key, $iv)
320     {
321         
322         $method = self::CIPHER.'-'.self::CIPHER_MODE;
323         
324         self::EnsureConstantExists("OPENSSL_RAW_DATA");
325         self::EnsureFunctionExists("openssl_encrypt");
326         $plaintext = openssl_decrypt(
327             $ciphertext,
328             $method,
329             $key,
330             OPENSSL_RAW_DATA,
331             $iv
332         );
333         if ($plaintext === FALSE) {
334             throw new CannotPerformOperationException();
335         }
336         
337         return $plaintext;
338     }
339
340     /*
341      * Returns a random binary string of length $octets bytes.
342      */
343     private static function SecureRandom($octets)
344     {
345         self::EnsureFunctionExists("openssl_random_pseudo_bytes");
346         $random = openssl_random_pseudo_bytes($octets, $crypto_strong);
347         if ($crypto_strong === FALSE) {
348             throw new CannotPerformOperationException();
349         } else {
350             return $random;
351         }
352     }
353
354     /*
355      * Use HKDF to derive multiple keys from one.
356      * http://tools.ietf.org/html/rfc5869
357      */
358     private static function HKDF($hash, $ikm, $length, $info = '', $salt = NULL)
359     {
360         // Find the correct digest length as quickly as we can.
361         $digest_length = self::MAC_BYTE_SIZE;
362         if ($hash != self::HASH_FUNCTION) {
363             $digest_length = self::our_strlen(hash_hmac($hash, '', '', true));
364         }
365
366         // Sanity-check the desired output length.
367         if (empty($length) || !is_int($length) ||
368             $length < 0 || $length > 255 * $digest_length) {
369             throw new CannotPerformOperationException();
370         }
371
372         // "if [salt] not provided, is set to a string of HashLen zeroes."
373         if (is_null($salt)) {
374             $salt = str_repeat("\x00", $digest_length);
375         }
376
377         // HKDF-Extract:
378         // PRK = HMAC-Hash(salt, IKM)
379         // The salt is the HMAC key.
380         $prk = hash_hmac($hash, $ikm, $salt, true);
381
382         // HKDF-Expand:
383
384         // This check is useless, but it serves as a reminder to the spec.
385         if (self::our_strlen($prk) < $digest_length) {
386             throw new CannotPerformOperationException();
387         }
388
389         // T(0) = ''
390         $t = '';
391         $last_block = '';
392         for ($block_index = 1; self::our_strlen($t) < $length; $block_index++) {
393             // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
394             $last_block = hash_hmac(
395                 $hash,
396                 $last_block . $info . chr($block_index),
397                 $prk,
398                 true
399             );
400             // T = T(1) | T(2) | T(3) | ... | T(N)
401             $t .= $last_block;
402         }
403
404         // ORM = first L octets of T
405         $orm = self::our_substr($t, 0, $length);
406         if ($orm === FALSE) {
407             throw new CannotPerformOperationException();
408         }
409         return $orm;
410     }
411
412     private static function VerifyHMAC($correct_hmac, $message, $key)
413     {
414         $message_hmac = hash_hmac(self::HASH_FUNCTION, $message, $key, true);
415
416         // We can't just compare the strings with '==', since it would make
417         // timing attacks possible. We could use the XOR-OR constant-time
418         // comparison algorithm, but I'm not sure if that's good enough way up
419         // here in an interpreted language. So we use the method of HMACing the 
420         // strings we want to compare with a random key, then comparing those.
421
422         // NOTE: This leaks information when the strings are not the same
423         // length, but they should always be the same length here. Enforce it:
424         if (self::our_strlen($correct_hmac) !== self::our_strlen($message_hmac)) {
425             throw new CannotPerformOperationException();
426         }
427
428         $blind = self::CreateNewRandomKey();
429         $message_compare = hash_hmac(self::HASH_FUNCTION, $message_hmac, $blind);
430         $correct_compare = hash_hmac(self::HASH_FUNCTION, $correct_hmac, $blind);
431         return $correct_compare === $message_compare;
432     }
433
434     private static function TestEncryptDecrypt()
435     {
436         $key = self::CreateNewRandomKey();
437         $data = "EnCrYpT EvErYThInG\x00\x00";
438
439         // Make sure encrypting then decrypting doesn't change the message.
440         $ciphertext = self::Encrypt($data, $key);
441         try {
442             $decrypted = self::Decrypt($ciphertext, $key);
443         } catch (InvalidCiphertextException $ex) {
444             // It's important to catch this and change it into a 
445             // CryptoTestFailedException, otherwise a test failure could trick
446             // the user into thinking it's just an invalid ciphertext!
447             throw new CryptoTestFailedException();
448         }
449         if($decrypted !== $data)
450         {
451             throw new CryptoTestFailedException();
452         }
453
454         // Modifying the ciphertext: Appending a string.
455         try {
456             self::Decrypt($ciphertext . "a", $key);
457             throw new CryptoTestFailedException();
458         } catch (InvalidCiphertextException $e) { /* expected */ }
459
460         // Modifying the ciphertext: Changing an IV byte.
461         try {
462             $ciphertext[0] = chr((ord($ciphertext[0]) + 1) % 256);
463             self::Decrypt($ciphertext, $key);
464             throw new CryptoTestFailedException();
465         } catch (InvalidCiphertextException $e) { /* expected */ }
466
467         // Decrypting with the wrong key.
468         $key = self::CreateNewRandomKey();
469         $data = "abcdef";
470         $ciphertext = self::Encrypt($data, $key);
471         $wrong_key = self::CreateNewRandomKey();
472         try {
473             self::Decrypt($ciphertext, $wrong_key);
474             throw new CryptoTestFailedException();
475         } catch (InvalidCiphertextException $e) { /* expected */ }
476
477         // Ciphertext too small (shorter than HMAC).
478         $key = self::CreateNewRandomKey();
479         $ciphertext = str_repeat("A", self::MAC_BYTE_SIZE - 1);
480         try {
481             self::Decrypt($ciphertext, $key);
482             throw new CryptoTestFailedException();
483         } catch (InvalidCiphertextException $e) { /* expected */ }
484     }
485
486     private static function HKDFTestVector()
487     {
488         // HKDF test vectors from RFC 5869
489
490         // Test Case 1
491         $ikm = str_repeat("\x0b", 22);
492         $salt = self::hexToBytes("000102030405060708090a0b0c");
493         $info = self::hexToBytes("f0f1f2f3f4f5f6f7f8f9");
494         $length = 42;
495         $okm = self::hexToBytes(
496             "3cb25f25faacd57a90434f64d0362f2a" .
497             "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" .
498             "34007208d5b887185865"
499         );
500         $computed_okm = self::HKDF("sha256", $ikm, $length, $info, $salt);
501         if ($computed_okm !== $okm) {
502             throw new CryptoTestFailedException();
503         }
504
505         // Test Case 7
506         $ikm = str_repeat("\x0c", 22);
507         $length = 42;
508         $okm = self::hexToBytes(
509             "2c91117204d745f3500d636a62f64f0a" .
510             "b3bae548aa53d423b0d1f27ebba6f5e5" .
511             "673a081d70cce7acfc48"
512         );
513         $computed_okm = self::HKDF("sha1", $ikm, $length);
514         if ($computed_okm !== $okm) {
515             throw new CryptoTestFailedException();
516         }
517
518     }
519
520     private static function HMACTestVector()
521     {
522         // HMAC test vector From RFC 4231 (Test Case 1)
523         $key = str_repeat("\x0b", 20);
524         $data = "Hi There";
525         $correct = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7";
526         if (hash_hmac(self::HASH_FUNCTION, $data, $key) != $correct) {
527             throw new CryptoTestFailedException();
528         }
529     }
530
531     private static function AESTestVector()
532     {
533         // AES CBC mode test vector from NIST SP 800-38A
534         $key = self::hexToBytes("2b7e151628aed2a6abf7158809cf4f3c");
535         $iv = self::hexToBytes("000102030405060708090a0b0c0d0e0f");
536         $plaintext = self::hexToBytes(
537             "6bc1bee22e409f96e93d7e117393172a" . 
538             "ae2d8a571e03ac9c9eb76fac45af8e51" .
539             "30c81c46a35ce411e5fbc1191a0a52ef" .
540             "f69f2445df4f9b17ad2b417be66c3710"
541         );
542         $ciphertext = self::hexToBytes(
543             "7649abac8119b246cee98e9b12e9197d" .
544             "5086cb9b507219ee95db113a917678b2" .
545             "73bed6b8e3c1743b7116e69e22229516" .
546             "3ff1caa1681fac09120eca307586e1a7" .
547             /* Block due to padding. Not from NIST test vector. 
548                 Padding Block: 10101010101010101010101010101010
549                 Ciphertext:    3ff1caa1681fac09120eca307586e1a7
550                            (+) 2fe1dab1780fbc19021eda206596f1b7 
551                            AES 8cb82807230e1321d3fae00d18cc2012
552              
553              */
554             "8cb82807230e1321d3fae00d18cc2012"
555         );
556
557         $computed_ciphertext = self::PlainEncrypt($plaintext, $key, $iv);
558         if ($computed_ciphertext !== $ciphertext) {
559             throw new CryptoTestFailedException();
560         }
561
562         $computed_plaintext = self::PlainDecrypt($ciphertext, $key, $iv);
563         if ($computed_plaintext !== $plaintext) {
564             throw new CryptoTestFailedException();
565         }
566     }
567
568     /* WARNING: Do not call this function on secrets. It creates side channels. */
569     private static function hexToBytes($hex_string)
570     {
571         return pack("H*", $hex_string);
572     }
573
574     private static function EnsureConstantExists($name)
575     {
576         if (!defined($name)) {
577             throw new CannotPerformOperationException();
578         }
579     }
580     
581     private static function EnsureFunctionExists($name)
582     {
583         if (!function_exists($name)) {
584             throw new CannotPerformOperationException();
585         }
586     }
587
588     /*
589      * We need these strlen() and substr() functions because when
590      * 'mbstring.func_overload' is set in php.ini, the standard strlen() and
591      * substr() are replaced by mb_strlen() and mb_substr().
592      */
593
594     private static function our_strlen($str)
595     {
596         if (function_exists('mb_strlen')) {
597             $length = mb_strlen($str, '8bit');
598             if ($length === FALSE) {
599                 throw new CannotPerformOperationException();
600             }
601             return $length;
602         } else {
603             return strlen($str);
604         }
605     }
606
607     private static function our_substr($str, $start, $length = NULL)
608     {
609         if (function_exists('mb_substr'))
610         {
611             // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP
612             // 5.3, so we have to find the length ourselves.
613             if (!isset($length)) {
614                 if ($start >= 0) {
615                     $length = self::our_strlen($str) - $start;
616                 } else {
617                     $length = -$start;
618                 }
619             }
620
621             return mb_substr($str, $start, $length, '8bit');
622         }
623
624         // Unlike mb_substr(), substr() doesn't accept NULL for length
625         if (isset($length)) {
626             return substr($str, $start, $length);
627         } else {
628             return substr($str, $start);
629         }
630     }
631
632 }
633
634 /*
635  * We want to catch all uncaught exceptions that come from the Crypto class,
636  * since by default, PHP will leak the key in the stack trace from an uncaught
637  * exception. This is a really ugly hack, but I think it's justified.
638  *
639  * Everything up to handler() getting called should be reliable, so this should
640  * reliably suppress the stack traces. The rest is just a bonus so that we don't
641  * make it impossible to debug other exceptions.
642  *
643  * This bit of code was adapted from: http://stackoverflow.com/a/7939492
644  */
645
646 class CryptoExceptionHandler
647 {
648     private $rethrow = NULL;
649
650     public function __construct()
651     {
652         set_exception_handler(array($this, "handler"));
653     }
654
655     public function handler($ex)
656     {
657         if (
658             $ex instanceof InvalidCiphertextException ||
659             $ex instanceof CannotPerformOperationException ||
660             $ex instanceof CryptoTestFailedException
661         ) {
662             echo "FATAL ERROR: Uncaught crypto exception. Suppresssing output.\n";
663         } else {
664             /* Re-throw the exception in the destructor. */
665             $this->rethrow = $ex;
666         }
667     }
668
669     public function __destruct() {
670         if ($this->rethrow) {
671             throw $this->rethrow;
672         }
673     }
674 }
675
676 $crypto_exception_handler_object_dont_touch_me = new CryptoExceptionHandler();
677