openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

Base2n.class.php (11181B)


      1 <?php
      2 namespace security;
      3 /**
      4  * Binary-to-text PHP Utilities
      5  *
      6  * @package     binary-to-text-php
      7  * @link        https://github.com/ademarre/binary-to-text-php
      8  * @author      Andre DeMarre
      9  * @copyright   2009-2013 Andre DeMarre
     10  * @license     http://opensource.org/licenses/MIT  MIT
     11  */
     12 
     13 /**
     14  * Class for binary-to-text encoding with a base of 2^n
     15  *
     16  * The Base2n class is for binary-to-text conversion. It employs a
     17  * generalization of the algorithms used by many encoding schemes that
     18  * use a fixed number of bits to encode each character. In other words,
     19  * the base is a power of 2.
     20  *
     21  * Earlier versions of this class were named
     22  * FixedBitNotation and FixedBitEncoding.
     23  *
     24  * @package binary-to-text-php
     25  */
     26 class Base2n
     27 {
     28     protected $_chars;
     29     protected $_bitsPerCharacter;
     30     protected $_radix;
     31     protected $_rightPadFinalBits;
     32     protected $_padFinalGroup;
     33     protected $_padCharacter;
     34     protected $_caseSensitive;
     35     protected $_charmap;
     36 
     37     /**
     38      * Constructor
     39      *
     40      * @param   integer $bitsPerCharacter   Bits to use for each encoded character
     41      * @param   string  $chars              Base character alphabet
     42      * @param   boolean $caseSensitive      To decode in a case-sensitive manner
     43      * @param   boolean $rightPadFinalBits  How to encode last character
     44      * @param   boolean $padFinalGroup      Add padding to end of encoded output
     45      * @param   string  $padCharacter       Character to use for padding
     46      *
     47      * @throws  InvalidArgumentException    for incompatible parameters
     48      */
     49     public function __construct(
     50         $bitsPerCharacter,
     51         $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_',
     52         $caseSensitive = TRUE, $rightPadFinalBits = FALSE,
     53         $padFinalGroup = FALSE, $padCharacter = '=')
     54     {
     55         // Ensure validity of $chars
     56         if (!is_string($chars) || ($charLength = strlen($chars)) < 2) {
     57             throw new InvalidArgumentException('$chars must be a string of at least two characters');
     58         }
     59 
     60         // Ensure validity of $padCharacter
     61         if ($padFinalGroup) {
     62             if (!is_string($padCharacter) || !isset($padCharacter[0])) {
     63                 throw new InvalidArgumentException('$padCharacter must be a string of one character');
     64             }
     65 
     66             if ($caseSensitive) {
     67                 $padCharFound = strpos($chars, $padCharacter[0]);
     68             } else {
     69                 $padCharFound = stripos($chars, $padCharacter[0]);
     70             }
     71 
     72             if ($padCharFound !== FALSE) {
     73                 throw new InvalidArgumentException('$padCharacter can not be a member of $chars');
     74             }
     75         }
     76 
     77         // Ensure validity of $bitsPerCharacter
     78         if (!is_int($bitsPerCharacter)) {
     79             throw new InvalidArgumentException('$bitsPerCharacter must be an integer');
     80         }
     81 
     82         if ($bitsPerCharacter < 1) {
     83             // $bitsPerCharacter must be at least 1
     84             throw new InvalidArgumentException('$bitsPerCharacter can not be less than 1');
     85 
     86         } elseif ($charLength < 1 << $bitsPerCharacter) {
     87             // Character length of $chars is too small for $bitsPerCharacter
     88             // Find greatest acceptable value of $bitsPerCharacter
     89             $bitsPerCharacter = 1;
     90             $radix = 2;
     91 
     92             while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) {
     93                 $bitsPerCharacter++;
     94             }
     95 
     96             $radix >>= 1;
     97             throw new InvalidArgumentException(
     98                     '$bitsPerCharacter can not be more than ' . $bitsPerCharacter
     99                   . ' given $chars length of ' . $charLength
    100                   . ' (max radix ' . $radix . ')');
    101 
    102         } elseif ($bitsPerCharacter > 8) {
    103             // $bitsPerCharacter must not be greater than 8
    104             throw new InvalidArgumentException('$bitsPerCharacter can not be greater than 8');
    105 
    106         } else {
    107             $radix = 1 << $bitsPerCharacter;
    108         }
    109 
    110         $this->_chars             = $chars;
    111         $this->_bitsPerCharacter  = $bitsPerCharacter;
    112         $this->_radix             = $radix;
    113         $this->_rightPadFinalBits = $rightPadFinalBits;
    114         $this->_padFinalGroup     = $padFinalGroup;
    115         $this->_padCharacter      = $padCharacter[0];
    116         $this->_caseSensitive     = $caseSensitive;
    117     }
    118 
    119     /**
    120      * Encode a string
    121      *
    122      * @param   string  $rawString  Binary data to encode
    123      * @return  string
    124      */
    125     public function encode($rawString)
    126     {
    127         // Unpack string into an array of bytes
    128         $bytes = unpack('C*', $rawString);
    129         $byteCount = count($bytes);
    130 
    131         $encodedString = '';
    132         $byte = array_shift($bytes);
    133         $bitsRead = 0;
    134         $oldBits = 0;
    135 
    136         $chars             = $this->_chars;
    137         $bitsPerCharacter  = $this->_bitsPerCharacter;
    138         $rightPadFinalBits = $this->_rightPadFinalBits;
    139         $padFinalGroup     = $this->_padFinalGroup;
    140         $padCharacter      = $this->_padCharacter;
    141 
    142         $charsPerByte = 8 / $bitsPerCharacter;
    143         $encodedLength = $byteCount * $charsPerByte;
    144 
    145         // Generate encoded output; each loop produces one encoded character
    146         for ($c = 0; $c < $encodedLength; $c++) {
    147 
    148             // Get the bits needed for this encoded character
    149             if ($bitsRead + $bitsPerCharacter > 8) {
    150                 // Not enough bits remain in this byte for the current character
    151                 // Save the remaining bits before getting the next byte
    152                 $oldBitCount = 8 - $bitsRead;
    153                 $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
    154                 $newBitCount = $bitsPerCharacter - $oldBitCount;
    155 
    156                 if (!$bytes) {
    157                     // Last bits; match final character and exit loop
    158                     if ($rightPadFinalBits) $oldBits <<= $newBitCount;
    159                     $encodedString .= $chars[$oldBits];
    160 
    161                     if ($padFinalGroup) {
    162                         // Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8
    163                         $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1);
    164                         $bytesPerGroup = $lcmMap[$bitsPerCharacter];
    165                         $pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte);
    166                         $encodedString .= str_repeat($padCharacter, $pads);
    167                     }
    168 
    169                     break;
    170                 }
    171 
    172                 // Get next byte
    173                 $byte = array_shift($bytes);
    174                 $bitsRead = 0;
    175 
    176             } else {
    177                 $oldBitCount = 0;
    178                 $newBitCount = $bitsPerCharacter;
    179             }
    180 
    181             // Read only the needed bits from this byte
    182             $bits = $byte >> 8 - ($bitsRead + ($newBitCount));
    183             $bits ^= $bits >> $newBitCount << $newBitCount;
    184             $bitsRead += $newBitCount;
    185 
    186             if ($oldBitCount) {
    187                 // Bits come from seperate bytes, add $oldBits to $bits
    188                 $bits = ($oldBits << $newBitCount) | $bits;
    189             }
    190 
    191             $encodedString .= $chars[$bits];
    192         }
    193 
    194         return $encodedString;
    195     }
    196 
    197     /**
    198      * Decode a string
    199      *
    200      * @param   string  $encodedString  Data to decode
    201      * @param   boolean $strict         Returns NULL if $encodedString contains an undecodable character
    202      * @return  string
    203      */
    204     public function decode($encodedString, $strict = FALSE)
    205     {
    206         if (!$encodedString || !is_string($encodedString)) {
    207             // Empty string, nothing to decode
    208             return '';
    209         }
    210 
    211         $chars             = $this->_chars;
    212         $bitsPerCharacter  = $this->_bitsPerCharacter;
    213         $radix             = $this->_radix;
    214         $rightPadFinalBits = $this->_rightPadFinalBits;
    215         $padFinalGroup     = $this->_padFinalGroup;
    216         $padCharacter      = $this->_padCharacter;
    217         $caseSensitive     = $this->_caseSensitive;
    218 
    219         // Get index of encoded characters
    220         if ($this->_charmap) {
    221             $charmap = $this->_charmap;
    222 
    223         } else {
    224             $charmap = array();
    225 
    226             for ($i = 0; $i < $radix; $i++) {
    227                 $charmap[$chars[$i]] = $i;
    228             }
    229 
    230             $this->_charmap = $charmap;
    231         }
    232 
    233         // The last encoded character is $encodedString[$lastNotatedIndex]
    234         $lastNotatedIndex = strlen($encodedString) - 1;
    235 
    236         // Remove trailing padding characters
    237         if ($padFinalGroup) {
    238             while ($encodedString[$lastNotatedIndex] === $padCharacter) {
    239                 $encodedString = substr($encodedString, 0, $lastNotatedIndex);
    240                 $lastNotatedIndex--;
    241             }
    242         }
    243 
    244         $rawString = '';
    245         $byte = 0;
    246         $bitsWritten = 0;
    247 
    248         // Convert each encoded character to a series of unencoded bits
    249         for ($c = 0; $c <= $lastNotatedIndex; $c++) {
    250 
    251             if (!$caseSensitive && !isset($charmap[$encodedString[$c]])) {
    252                 // Encoded character was not found; try other case
    253                 if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) {
    254                     $charmap[$encodedString[$c]] = $charmap[$cUpper];
    255 
    256                 } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) {
    257                     $charmap[$encodedString[$c]] = $charmap[$cLower];
    258                 }
    259             }
    260 
    261             if (isset($charmap[$encodedString[$c]])) {
    262                 $bitsNeeded = 8 - $bitsWritten;
    263                 $unusedBitCount = $bitsPerCharacter - $bitsNeeded;
    264 
    265                 // Get the new bits ready
    266                 if ($bitsNeeded > $bitsPerCharacter) {
    267                     // New bits aren't enough to complete a byte; shift them left into position
    268                     $newBits = $charmap[$encodedString[$c]] << $bitsNeeded - $bitsPerCharacter;
    269                     $bitsWritten += $bitsPerCharacter;
    270 
    271                 } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) {
    272                     // Zero or more too many bits to complete a byte; shift right
    273                     $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount;
    274                     $bitsWritten = 8; //$bitsWritten += $bitsNeeded;
    275 
    276                 } else {
    277                     // Final bits don't need to be shifted
    278                     $newBits = $charmap[$encodedString[$c]];
    279                     $bitsWritten = 8;
    280                 }
    281 
    282                 $byte |= $newBits;
    283 
    284                 if ($bitsWritten === 8 || $c === $lastNotatedIndex) {
    285                     // Byte is ready to be written
    286                     $rawString .= pack('C', $byte);
    287 
    288                     if ($c !== $lastNotatedIndex) {
    289                         // Start the next byte
    290                         $bitsWritten = $unusedBitCount;
    291                         $byte = ($charmap[$encodedString[$c]] ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten;
    292                     }
    293                 }
    294 
    295             } elseif ($strict) {
    296                 // Unable to decode character; abort
    297                 return NULL;
    298             }
    299         }
    300 
    301         return $rawString;
    302     }
    303 }
    304 ?>