1 <?php
2 /**
3 * @package FrameworkOnFramework
4 * @subpackage encrypt
5 * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
6 * @license GNU General Public License version 2 or later; see LICENSE.txt
7 */
8 defined('FOF_INCLUDED') or die;
9
10 /**
11 * FOFEncryptBase32
12 *
13 * @package FrameworkOnFramework
14 * @since 1.0
15 */
16 class FOFEncryptBase32
17 {
18 /**
19 * CSRFC3548
20 *
21 * The character set as defined by RFC3548
22 * @link http://www.ietf.org/rfc/rfc3548.txt
23 */
24 const CSRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
25
26 /**
27 * str2bin
28 *
29 * Converts any ascii string to a binary string
30 *
31 * @param string $str The string you want to convert
32 *
33 * @return string String of 0's and 1's
34 */
35 private function str2bin($str)
36 {
37 $chrs = unpack('C*', $str);
38
39 return vsprintf(str_repeat('%08b', count($chrs)), $chrs);
40 }
41
42 /**
43 * bin2str
44 *
45 * Converts a binary string to an ascii string
46 *
47 * @param string $str The string of 0's and 1's you want to convert
48 *
49 * @return string The ascii output
50 *
51 * @throws Exception
52 */
53 private function bin2str($str)
54 {
55 if (strlen($str) % 8 > 0)
56 {
57 throw new Exception('Length must be divisible by 8');
58 }
59
60 if (!preg_match('/^[01]+$/', $str))
61 {
62 throw new Exception('Only 0\'s and 1\'s are permitted');
63 }
64
65 preg_match_all('/.{8}/', $str, $chrs);
66 $chrs = array_map('bindec', $chrs[0]);
67
68 // I'm just being slack here
69 array_unshift($chrs, 'C*');
70
71 return call_user_func_array('pack', $chrs);
72 }
73
74 /**
75 * fromBin
76 *
77 * Converts a correct binary string to base32
78 *
79 * @param string $str The string of 0's and 1's you want to convert
80 *
81 * @return string String encoded as base32
82 *
83 * @throws exception
84 */
85 private function fromBin($str)
86 {
87 if (strlen($str) % 8 > 0)
88 {
89 throw new Exception('Length must be divisible by 8');
90 }
91
92 if (!preg_match('/^[01]+$/', $str))
93 {
94 throw new Exception('Only 0\'s and 1\'s are permitted');
95 }
96
97 // Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out
98 $str = preg_replace('/(.{5})/', '000$1', $str);
99
100 // We need a string divisible by 5
101 $length = strlen($str);
102 $rbits = $length & 7;
103
104 if ($rbits > 0)
105 {
106 // Excessive bits need to be padded
107 $ebits = substr($str, $length - $rbits);
108 $str = substr($str, 0, $length - $rbits);
109 $str .= "000$ebits" . str_repeat('0', 5 - strlen($ebits));
110 }
111
112 preg_match_all('/.{8}/', $str, $chrs);
113 $chrs = array_map(array($this, '_mapcharset'), $chrs[0]);
114
115 return join('', $chrs);
116 }
117
118 /**
119 * toBin
120 *
121 * Accepts a base32 string and returns an ascii binary string
122 *
123 * @param string $str The base32 string to convert
124 *
125 * @return string Ascii binary string
126 *
127 * @throws Exception
128 */
129 private function toBin($str)
130 {
131 if (!preg_match('/^[' . self::CSRFC3548 . ']+$/', $str))
132 {
133 throw new Exception('Must match character set');
134 }
135
136 // Convert the base32 string back to a binary string
137 $str = join('', array_map(array($this, '_mapbin'), str_split($str)));
138
139 // Remove the extra 0's we added
140 $str = preg_replace('/000(.{5})/', '$1', $str);
141
142 // Unpad if nessicary
143 $length = strlen($str);
144 $rbits = $length & 7;
145
146 if ($rbits > 0)
147 {
148 $str = substr($str, 0, $length - $rbits);
149 }
150
151 return $str;
152 }
153
154 /**
155 * fromString
156 *
157 * Convert any string to a base32 string
158 * This should be binary safe...
159 *
160 * @param string $str The string to convert
161 *
162 * @return string The converted base32 string
163 */
164 public function encode($str)
165 {
166 return $this->fromBin($this->str2bin($str));
167 }
168
169 /**
170 * toString
171 *
172 * Convert any base32 string to a normal sctring
173 * This should be binary safe...
174 *
175 * @param string $str The base32 string to convert
176 *
177 * @return string The normal string
178 */
179 public function decode($str)
180 {
181 $str = strtoupper($str);
182
183 return $this->bin2str($this->tobin($str));
184 }
185
186 /**
187 * _mapcharset
188 *
189 * Used with array_map to map the bits from a binary string
190 * directly into a base32 character set
191 *
192 * @param string $str The string of 0's and 1's you want to convert
193 *
194 * @return string Resulting base32 character
195 *
196 * @access private
197 */
198 private function _mapcharset($str)
199 {
200 // Huh!
201 $x = self::CSRFC3548;
202
203 return $x[bindec($str)];
204 }
205
206 /**
207 * _mapbin
208 *
209 * Used with array_map to map the characters from a base32
210 * character set directly into a binary string
211 *
212 * @param string $chr The caracter to map
213 *
214 * @return string String of 0's and 1's
215 *
216 * @access private
217 */
218 private function _mapbin($chr)
219 {
220 return sprintf('%08b', strpos(self::CSRFC3548, $chr));
221 }
222 }
223