Index: bakery.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/bakery/bakery.module,v retrieving revision 1.53 diff -u -p -r1.53 bakery.module --- bakery.module 15 Apr 2010 16:00:14 -0000 1.53 +++ bakery.module 25 Apr 2010 06:48:29 -0000 @@ -131,7 +131,7 @@ function bakery_user_update(&$edit, $acc $payload['uid'] = $account->uid; $payload['category'] = $category; $payload['signature'] = hash_hmac('sha256', $payload['data'] . '/' . $payload['uid'] .'/'. $payload['timestamp'], $key); - $payload = drupal_http_build_query(array('stroopwafel' => bakery_mix(serialize($payload), 1))); + $payload = drupal_http_build_query(array('stroopwafel' => bakery_mix(serialize($payload)))); unset($_SESSION['bakery']); // now update the slaves $slaves = variable_get('bakery_slaves', array()); @@ -376,13 +376,15 @@ function _bakery_validate_cookie($type = return; } - $cookie = unserialize(bakery_mix($_COOKIE[$type], 0)); - $signature = hash_hmac('sha256', $cookie['name'] . '/' . $cookie['mail'] . '/' . $cookie['timestamp'], $key); - $valid = FALSE; - if ($signature == $cookie['signature'] && $cookie['timestamp'] + variable_get('bakery_freshness', '3600') >= $_SERVER['REQUEST_TIME']) { - $valid = TRUE; + if ($cookie_serialized = bakery_unmix($_COOKIE[$type])) { + $cookie = unserialize($cookie_serialized); + + if ($cookie['timestamp'] + variable_get('bakery_freshness', '3600') >= $_SERVER['REQUEST_TIME']) { + $valid = TRUE; + } + } return $valid ? $cookie : $valid; @@ -547,15 +549,15 @@ function bakery_taste_stroopwafel_cookie $valid = FALSE; if ($payload) { - $cookie = unserialize(bakery_mix($payload, 0)); - $key = variable_get('bakery_key', ''); - $signature = hash_hmac('sha256', $cookie['data'] . '/' . $cookie['uid'] . '/' . $cookie['timestamp'], $key); + if ($cookie_serialized = bakery_unmix($payload)) { + $cookie = unserialize($cookie_serialized); - if ($signature == $cookie['signature'] && $cookie['timestamp'] + variable_get('bakery_freshness', '3600') >= $_SERVER['REQUEST_TIME']) { - $valid = TRUE; - $_SESSION['bakery'] = unserialize($cookie['data']); - $_SESSION['bakery']['uid'] = $cookie['uid']; - $_SESSION['bakery']['category'] = $cookie['category']; + if ($cookie['timestamp'] + variable_get('bakery_freshness', '3600') >= $_SERVER['REQUEST_TIME']) { + $valid = TRUE; + $_SESSION['bakery'] = unserialize($cookie['data']); + $_SESSION['bakery']['uid'] = $cookie['uid']; + $_SESSION['bakery']['category'] = $cookie['category']; + } } } @@ -575,8 +577,7 @@ function _bakery_bake_chocolatechip_cook $cookie['master'] = variable_get('bakery_is_master', 0); $cookie['calories'] = 480; $cookie['timestamp'] = $_SERVER['REQUEST_TIME']; - $cookie['signature'] = hash_hmac('sha256', $cookie['name'] . '/' . $cookie['mail'] . '/' . $cookie['timestamp'], $key); - setcookie('CHOCOLATECHIP', bakery_mix(serialize($cookie), 1), $_SERVER['REQUEST_TIME'] + variable_get('bakery_freshness', '3600'), '/', variable_get('bakery_domain', '')); + setcookie('CHOCOLATECHIP', bakery_mix(serialize($cookie)), $_SERVER['REQUEST_TIME'] + variable_get('bakery_freshness', '3600'), '/', variable_get('bakery_domain', '')); } } @@ -598,8 +599,7 @@ function bakery_bake_oatmeal_cookie() { $cookie['destination'] = $base_url .'/'. $destination; $cookie['calories'] = 320; $cookie['timestamp'] = $_SERVER['REQUEST_TIME']; - $cookie['signature'] = hash_hmac('sha256', $cookie['name'] . '/' . $cookie['mail'] . '/' . $cookie['timestamp'], $key); - setcookie('OATMEAL', bakery_mix(serialize($cookie), 1), $_SERVER['REQUEST_TIME'] + variable_get('bakery_freshness', '3600'), '/', variable_get('bakery_domain', '')); + setcookie('OATMEAL', bakery_mix(serialize($cookie)), $_SERVER['REQUEST_TIME'] + variable_get('bakery_freshness', '3600'), '/', variable_get('bakery_domain', '')); } unset($_GET['destination']); @@ -658,34 +658,156 @@ function _bakery_eat_cookie($type = 'CHO } /** - * Encryption + * Encrypt a string using the bakery encryption functions. + * + * See _bakery_encrypt_decrypt() for details. * - * @param $text, The text that you want to encrypt. - * @param $crypt = 1 if you want to crypt, or 0 if you want to decrypt. + * @param $text + * The payload of data to encrypt. + * @param $base64encode + * Optionally encode the encrypted using base64 encoding, defaults to TRUE. */ -function bakery_mix($text, $crypt) { - $key = variable_get('bakery_key', ''); - - $td = mcrypt_module_open('rijndael-128', '', 'ecb', ''); - $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); +function bakery_mix($text, $base64encode = TRUE) { + return _bakery_encrypt_decrypt($text, TRUE, $base64encode); +} - $key = substr($key, 0, mcrypt_enc_get_key_size($td)); +/** + * Decrypt a string using the bakery encryption functions. + * + * See _bakery_encrypt_decrypt() for details. + * + * @param $text + * The payload of data to decrypt. + * @param $base64encode + * Optionally decode the $text using base64 encoding, defaults to TRUE. + */ +function bakery_unmix($text, $base64encode = TRUE) { + return _bakery_encrypt_decrypt($text, FALSE, $base64encode); +} - mcrypt_generic_init($td, $key, $iv); +/** + * Bakery encryption/decryption. + * + * This function will add a IV and Message Authentication Code (MAC). Basically + * you can be sure that if something decrypts properly then it hasn't been + * tampered with either. + * + * @param $text + * The text that you want to encrypt/decrypt. + * @param $crypt + * TRUE if you want to crypt, or FALSE if you want to decrypt. + * @param $base64encode + * Optionally encode/decode the given $text using base64 encoding. + * @return + * FALSE on error/validation fail or the encrypted plaintext or decrypted + * message. After encryption the returned string has the form: + * IV | CRYPTO_TEXT | MAC + */ +function _bakery_encrypt_decrypt($text, $crypt, $base64encode = TRUE) { + $key = variable_get('bakery_key', ''); - if($crypt) { + // If we're doing base64 encoding, need to decode if decrypting: + if ($base64encode && !$crypt) { + $text = base64_decode($text); + } + + // We'll use AES in CTR mode + $td = mcrypt_module_open('rijndael-256', '', 'ctr', ''); + + // Compute the key: + $computed_key = bakery_pbkdf2($key, 'VKtl]c}~3;WJ*QEvn', 1000, mcrypt_enc_get_key_size($td)); + + if ($crypt) { + // This is encryption: + // Compute an IV. This IV should only EVER be used once: + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); + mcrypt_generic_init($td, $computed_key, $iv); + // Do the encryption: $encrypted_data = mcrypt_generic($td, $text); + // Append the IV to the data: + $encrypted_data = $iv . $encrypted_data; + // Now compute the MAC: + $mac = bakery_pbkdf2($encrypted_data, $computed_key, 1000, 32); + // Append the MAC to the IV and excrypted payload + $encrypted_data .= $mac; + } else { - $encrypted_data = mdecrypt_generic($td, $text); + // This is decryption: + // Get the IV: + $iv = substr($text, 0, mcrypt_enc_get_iv_size($td)); + // Get the MAC: + $message_mac = substr($text, strlen($text) - 32); + // Get the payload of encrypted data: + $encrypted_data = substr($text, mcrypt_enc_get_iv_size($td), strlen($text) - (32 + mcrypt_enc_get_iv_size($td))); + // Re-compute the MAC: + $mac = bakery_pbkdf2($iv . $encrypted_data, $computed_key, 1000, 32); + + // Make sure that MAC in the message ($message_mac) matches the computed $mac + if ($mac != $message_mac) { + return FALSE; + } + + // Now do the decryption: + mcrypt_generic_init($td, $computed_key, $iv); + $encrypted_data = mdecrypt_generic($td, $encrypted_data); + } - mcrypt_generic_deinit($td); - mcrypt_module_close($td); + // If we're doing base64 encoding, need to encode if encrypting: + if ($base64encode && $crypt) { + $encrypted_data = base64_encode($encrypted_data); + } return $encrypted_data; } + +/** + * PBKDF2 Implementation (as described in RFC 2898); + * + * Slightly modified version of the code at: + * http://www.itnewb.com/v/PHP-Encryption-Decryption-Using-the-MCrypt-Library-libmcrypt + * + * @param $password. + * @param $salt + * @param $iteration_count + * Iteration count (use 1000 or higher.) + * @param $key_length + * Derived key length. + * @param $a + * Hash algorithm. + * + * @return + * Derived key +*/ +function bakery_pbkdf2($password, $salt, $iteration_count, $key_length, $a = 'sha256') { + + $hl = strlen(hash($a, NULL, TRUE)); // Hash length + $kb = ceil($key_length / $hl); // Key blocks to compute + $dk = ''; // Derived key + + // Create key + for ($block = 1; $block <= $kb; $block++) { + + // Initial hash for this block + $ib = $b = hash_hmac($a, $salt . pack('N', $block), $password, TRUE); + + // Perform block iterations + for ($i = 1; $i < $iteration_count; $i++) { + + // XOR each iterate + $ib ^= ($b = hash_hmac($a, $b, $password, TRUE)); + + } + + $dk .= $ib; // Append iterated block + } + + // Return derived key of correct length + return substr($dk, 0, $key_length); +} + /** * Perform standard Drupal login operations for a user object. * @@ -804,7 +926,7 @@ function bakery_uncrumble($form, &$form_ ->where("LOWER(u.mail) = LOWER(:mail)", array(':mail' => $cookie['mail'])); $result = $query->execute(); $samemail = $result->fetchObject(); - + $query = db_select('users', 'u') ->fields('u', array('uid', 'name', 'mail')) ->condition('u.uid', 0, '!=')