You are here

urllogin_security.inc in urllogin 6

Include file for urllogin security functions. This module is designed for easy drop-in replacement where an alternative encryption model is required.

File

urllogin_security.inc
View source
<?php

/**
 * @file
 * Include file for urllogin security functions.
 * This module is designed for easy drop-in replacement where an alternative
 * encryption model is required.
 */

/**
 * Converts integer to 4-character string containing hashed values.
 *
 * The characters contain 8-bit binary values rather than normal characters.
 * If integer is greater than a 32 bit value, only the lower 32 bits are used.
 *
 * @param $i
 *   Integer to generate hash string.
 *
 * @return
 *   Four byte string of characters, each capable of 256 possible values.
 */
function _urllogin_inthash($i) {
  $s = hash('sha256', $i & 0x7fffffff, TRUE);
  return ord($s[0]) << 24 | ord($s[1]) << 16 | ord($s[2]) << 8 | ord($s[3]);
}

/**
 * Encrypts a pair of integers
 *
 * The encryption uses a "butterfly" technique similar to the DES's Feistel
 * scheme.
 * See @link http://en.wikipedia.org/wiki/Data_Encryption_Standard for more details of the DES @endlink
 * Instead of the Feistel function, sha256 is used. (This is probably way overkill
 * but it is easy to code.)
 * Another encryption algorithm can be plugged in here if desired.
 *
 * @param $i
 *   First Integer, passed by reference
 *
 * @param $j
 *   Second Integer, passed by reference
 *
 * @param $x
 *   Extra byte, passed by reference
 *
 * @param $passkey
 *   String containing encryption key phrase
 *
 * Nothin is returned.
 */
function urllogin_encrypt(&$i, &$j, &$x, $passkey) {
  $k = hash('sha256', $passkey, TRUE);

  // sha256 produces 32 bytes, so could do 16 interations
  for ($iter = 0; $iter < 8; $iter++) {
    $tmp = $i;
    $i = $j ^ _urllogin_inthash($i) ^ (ord($k[$iter * 2]) << 8 | ord($k[$iter * 2 + 1]));
    $j = $tmp;
  }
  $x = ($i ^ $j ^ ord($k[16])) & 0xff;

  // extra byte for added security
}

/**
 * Decrypts a pair of integers
 *
 * Exact reverse of encryption:
 * The values of $i and $j are swapped.
 * The key is applied in the reverse order to encryption.
 *
 * @param $j
 *   First Integer, passed by reference
 *
 * @param $i
 *   Second Integer, passed by reference
 *
 * @param $x
 *   Extra byte, passed by reference
 *
 * @param $passkey
 *   String containing encryption key phrase
 *
 * @return
 *   TRUE if successful, FALSE if extra byte fails
 *   Note that a TRUE return does not mean security checks are past.
 *   This is just an added level of security to help with diagnostics.
 */
function urllogin_decrypt(&$j, &$i, &$x, $passkey) {
  $k = hash('sha256', $passkey, TRUE);

  // sha256 produces 32 bytes, so could do 16 interations
  if ($x != (($i ^ $j ^ ord($k[16])) & 0xff)) {
    return FALSE;
  }

  // extra byte for added security
  for ($iter = 0; $iter < 8; $iter++) {
    $tmp = $i;
    $i = $j ^ _urllogin_inthash($i) ^ (ord($k[14 - $iter * 2]) << 8 | ord($k[14 - $iter * 2 + 1]));
    $j = $tmp;
  }
  return TRUE;
}

/**
 * Converts a pair of integers plus an extra byte into a base64url encoded string
 *
 * If integer is greater than a 32 bit value, only the lower 32 bits are used.
 *
 * @param $i
 *   First Integer
 *
 * @param $j
 *   Second Integer
 *
 * @param $x
 *   Extra byte
 *
 * @return
 *   Return base64url encoded string which will be 11 characters long
 *   since the '=' is stripped off the end.
 */
function urllogin_base64enc($i, $j, $x) {
  return strtr(base64_encode(chr($i >> 24 & 0xff) . chr($i >> 16 & 0xff) . chr($i >> 8 & 0xff) . chr($i & 0xff) . chr($j >> 24 & 0xff) . chr($j >> 16 & 0xff) . chr($j >> 8 & 0xff) . chr($j & 0xff) . chr($x & 0xff)), '+/=', '-_,');

  // modify to use base64url encoding
}

/**
 * Converts a base64url encoded string into a pair of integers plus an extra byte
 *
 * @param $i
 *   First Integer, passed by reference
 *
 * @param $j
 *   Second Integer, passed by reference
 *
 * @param $x
 *   Extra byte, passed by reference
 *
 * @param $urlstr
 *   base64 encoded string with the '=' stripped off the end
 *
 * @return
 *   Return TRUE if successful, FALSE if $urlstr was invalid base64url
 */
function urllogin_base64dec(&$i, &$j, &$x, $urlstr) {
  if (strlen($urlstr) != 12) {

    // do not use drupal_strlen because this is a binary string, not UTF
    return FALSE;

    // URL wrong length
  }

  // modify to use base64url decoding and decode
  if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
    $s = base64_decode(strtr($urlstr, '-_,', '+/='), TRUE);
  }
  else {
    $s = base64_decode(strtr($urlstr, '-_,', '+/='));

    // before PHP 5.2, no STRICT parameter
  }
  if ($s == FALSE) {
    return FALSE;
  }
  $i = ord($s[0]) << 24 | ord($s[1]) << 16 | ord($s[2]) << 8 | ord($s[3]);
  $j = ord($s[4]) << 24 | ord($s[5]) << 16 | ord($s[6]) << 8 | ord($s[7]);
  $x = ord($s[8]);
  return TRUE;
}

/**
 * Encodes a user ID into an encoded url string.
 *
 * @param $uid
 *   user ID to be encoded
 *
 * @param $codekey
 *   Integer containing current active code
 *
 * @param $passkey
 *   String containing encryption key phrase
 *
 * @return
 *    encoded url string
 */
function urllogin_encode($uid, $codekey, $passkey) {

  // first encrypt the two values
  urllogin_encrypt($uid, $codekey, $x, $passkey);

  // then encode them into a string suitable for embedding into a URL
  return urllogin_base64enc($uid, $codekey, $x);
}

/**
 * Decodes an encoded url string into a user ID and tests validity.
 * If the uid matches the current one supplied, then it is valid even if link is expired.
 * This is so that the user for whom the link is intended does not get an error message
 * if they are logged in and click on an old link, but instead still get redirected.
 *
 * @param string $urlstr
 *   encoded url string
 *
 * @param int $codekey
 *   Integer containing current active code (maximum allowable value)
 *
 * @param int $codemin
 *   Integer containing minimum allowable value of code
 *
 * @param string $passkey
 *   String containing encryption key phrase
 *
 * @param string $errormsg
 *   Contains error message if function fails.
 *
 * @param int $currentuid
 *   Contains optional current uid
 *
 * @return int
 *   Return UID if successful, -1 if fail, -2 if link expired
 */
function urllogin_decode($urlstr, $codekey, $codemin, $passkey, &$errormsg, $currentuid = -1) {
  if (!urllogin_base64dec($i, $j, $x, $urlstr)) {
    $errormsg = 'Invalid Base64 URL string';
    return -1;
  }
  if (!urllogin_decrypt($i, $j, $x, $passkey)) {
    $errormsg = "Invalid access string";
    return -1;
  }
  if (($j < $codemin or $j > $codekey) and $i != $currentuid) {
    $errormsg = "code: {$j} outside permitted range: {$codemin} to {$codekey}";
    return -2;
  }
  return $i;
}

Functions

Namesort descending Description
urllogin_base64dec Converts a base64url encoded string into a pair of integers plus an extra byte
urllogin_base64enc Converts a pair of integers plus an extra byte into a base64url encoded string
urllogin_decode Decodes an encoded url string into a user ID and tests validity. If the uid matches the current one supplied, then it is valid even if link is expired. This is so that the user for whom the link is intended does not get an error message if they are…
urllogin_decrypt Decrypts a pair of integers
urllogin_encode Encodes a user ID into an encoded url string.
urllogin_encrypt Encrypts a pair of integers
_urllogin_inthash Converts integer to 4-character string containing hashed values.