Overview

Packages

  • Secret

Classes

  • Horde_Secret
  • Horde_Secret_Exception
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Provides an API for encrypting and decrypting small pieces of data with the
  4:  * use of a shared key stored in a cookie.
  5:  *
  6:  * Copyright 1999-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (LGPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 10:  *
 11:  * @author   Chuck Hagenbuch <chuck@horde.org>
 12:  * @author   Michael Slusarz <slusarz@horde.org>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/lgpl21 LGPL
 15:  * @package  Secret
 16:  */
 17: class Horde_Secret
 18: {
 19:     /** Generic, default keyname. */
 20:     const DEFAULT_KEY = 'generic';
 21: 
 22:     /**
 23:      * Configuration parameters.
 24:      *
 25:      * @var array
 26:      */
 27:     protected $_params = array(
 28:         'cookie_domain' => '',
 29:         'cookie_path' => '',
 30:         'cookie_ssl' => false,
 31:         'session_name' => 'horde_secret'
 32:     );
 33: 
 34:     /**
 35:      * Cipher cache.
 36:      *
 37:      * @var array
 38:      */
 39:     protected $_cipherCache = array();
 40: 
 41:     /**
 42:      * Key cache.
 43:      *
 44:      * @var array
 45:      */
 46:     protected $_keyCache = array();
 47: 
 48:     /**
 49:      * Constructor.
 50:      *
 51:      * @param array $params  Configuration parameters:
 52:      *   - cookie_domain: (string) The cookie domain.
 53:      *   - cookie_path: (string) The cookie path.
 54:      *   - cookie_ssl: (boolean) Only transmit cookie securely?
 55:      *   - session_name: (string) The cookie session name.
 56:      */
 57:     public function __construct($params = array())
 58:     {
 59:         $this->_params = array_merge($this->_params, $params);
 60:     }
 61: 
 62:     /**
 63:      * Take a small piece of data and encrypt it with a key.
 64:      *
 65:      * @param string $key      The key to use for encryption.
 66:      * @param string $message  The plaintext message.
 67:      *
 68:      * @return string  The ciphertext message.
 69:      * @throws Horde_Secret_Exception
 70:      */
 71:     public function write($key, $message)
 72:     {
 73:         $message = (string)$message;
 74:         if (strlen($key) && strlen($message)) {
 75:             return $this->_getCipherOb($key)->encrypt($message);
 76:         } else {
 77:             return '';
 78:         }
 79:     }
 80: 
 81:     /**
 82:      * Decrypt a message encrypted with write().
 83:      *
 84:      * @param string $key      The key to use for decryption.
 85:      * @param string $message  The ciphertext message.
 86:      *
 87:      * @return string  The plaintext message.
 88:      * @throws Horde_Secret_Exception
 89:      */
 90:     public function read($key, $ciphertext)
 91:     {
 92:         $ciphertext = (string)$ciphertext;
 93:         if (strlen($key) && strlen($ciphertext)) {
 94:             return rtrim($this->_getCipherOb($key)->decrypt($ciphertext), "\0");
 95:         } else {
 96:             return '';
 97:         }
 98:     }
 99: 
100:     /**
101:      * Returns the cached crypt object.
102:      *
103:      * @param string $key  The key to use for [de|en]cryption. Only the first
104:      *                     56 bytes of this string is used.
105:      *
106:      * @return Crypt_Blowfish  The crypt object.
107:      * @throws Horde_Secret_Exception
108:      */
109:     protected function _getCipherOb($key)
110:     {
111:         if (!is_string($key)) {
112:             throw new Horde_Secret_Exception('Key must be a string', 2);
113:         }
114: 
115:         if (!strlen($key)) {
116:             throw new Horde_Secret_Exception('Key must be non-zero.', 3);
117:         }
118: 
119:         $key = substr($key, 0, 56);
120: 
121:         $idx = hash('md5', $key);
122: 
123:         if (!isset($this->_cipherCache[$idx])) {
124:             if (!class_exists('Crypt_Blowfish')) {
125:                 throw new Horde_Secret_Exception('Crypt_Blowfish library not found.');
126:             }
127:             $this->_cipherCache[$idx] = new Crypt_Blowfish($key);
128:         }
129: 
130:         return $this->_cipherCache[$idx];
131:     }
132: 
133:     /**
134:      * Generate a secret key (for encryption), either using a random
135:      * md5 string and storing it in a cookie if the user has cookies
136:      * enabled, or munging some known values if they don't.
137:      *
138:      * @param string $keyname  The name of the key to set.
139:      *
140:      * @return string  The secret key that has been generated.
141:      */
142:     public function setKey($keyname = self::DEFAULT_KEY)
143:     {
144:         $set = true;
145: 
146:         if (isset($_COOKIE[$this->_params['session_name']])) {
147:             if (isset($_COOKIE[$keyname . '_key'])) {
148:                 $key = $_COOKIE[$keyname . '_key'];
149:                 $set = false;
150:             } else {
151:                 $key = $_COOKIE[$keyname . '_key'] = uniqid(mt_rand());
152:             }
153:         } else {
154:             $key = session_id();
155:         }
156: 
157:         if ($set) {
158:             $this->_setCookie($keyname, $key);
159:         }
160: 
161:         return $key;
162:     }
163: 
164:     /**
165:      * Return a secret key, either from a cookie, or if the cookie
166:      * isn't there, assume we are using a munged version of a known
167:      * base value.
168:      *
169:      * @param string $keyname  The name of the key to get.
170:      *
171:      * @return string  The secret key.
172:      */
173:     public function getKey($keyname = self::DEFAULT_KEY)
174:     {
175:         if (!isset($this->_keyCache[$keyname])) {
176:             if (isset($_COOKIE[$keyname . '_key'])) {
177:                 $key = $_COOKIE[$keyname . '_key'];
178:             } else {
179:                 $key = session_id();
180:                 $this->_setCookie($keyname, $key);
181:             }
182: 
183:             $this->_keyCache[$keyname] = $key;
184:         }
185: 
186:         return $this->_keyCache[$keyname];
187:     }
188: 
189:     /**
190:      * Clears a secret key entry from the current cookie.
191:      *
192:      * @param string $keyname  The name of the key to clear.
193:      *
194:      * @return boolean  True if key existed, false if not.
195:      */
196:     public function clearKey($keyname = self::DEFAULT_KEY)
197:     {
198:         if (isset($_COOKIE[$this->_params['session_name']]) &&
199:             isset($_COOKIE[$keyname . '_key'])) {
200:             $this->_setCookie($keyname, false);
201:             return true;
202:         }
203: 
204:         return false;
205:     }
206: 
207:     /**
208:      * Sets the cookie with the given keyname/key.
209:      *
210:      * @param string $keyname  The name of the key to set.
211:      * @param string $key      The key to use for encryption.
212:      */
213:     protected function _setCookie($keyname, $key)
214:     {
215:         @setcookie(
216:             $keyname . '_key',
217:             $key,
218:             0,
219:             $this->_params['cookie_path'],
220:             $this->_params['cookie_domain'],
221:             $this->_params['cookie_ssl'],
222:             true
223:         );
224: 
225:         if ($key === false) {
226:             unset($_COOKIE[$keyname], $this->_keyCache[$keyname]);
227:         } else {
228:             $_COOKIE[$keyname] = $this->_keyCache[$keyname] = $key;
229:         }
230:     }
231: 
232: }
233: 
API documentation generated by ApiGen