|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Core_SipHash', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Class ParagonIE_SodiumCompat_Core_SipHash |
|
9 * |
|
10 * Only uses 32-bit arithmetic, while the original SipHash used 64-bit integers |
|
11 */ |
|
12 class ParagonIE_Sodium_Core_SipHash extends ParagonIE_Sodium_Core_Util |
|
13 { |
|
14 /** |
|
15 * @internal You should not use this directly from another application |
|
16 * |
|
17 * @param int[] $v |
|
18 * @return int[] |
|
19 */ |
|
20 public static function sipRound(array $v) |
|
21 { |
|
22 # v0 += v1; |
|
23 list($v[0], $v[1]) = self::add( |
|
24 array($v[0], $v[1]), |
|
25 array($v[2], $v[3]) |
|
26 ); |
|
27 |
|
28 # v1=ROTL(v1,13); |
|
29 list($v[2], $v[3]) = self::rotl_64($v[2], $v[3], 13); |
|
30 |
|
31 # v1 ^= v0; |
|
32 $v[2] ^= $v[0]; |
|
33 $v[3] ^= $v[1]; |
|
34 |
|
35 # v0=ROTL(v0,32); |
|
36 list($v[0], $v[1]) = self::rotl_64((int) $v[0], (int) $v[1], 32); |
|
37 |
|
38 # v2 += v3; |
|
39 list($v[4], $v[5]) = self::add( |
|
40 array($v[4], $v[5]), |
|
41 array($v[6], $v[7]) |
|
42 ); |
|
43 |
|
44 # v3=ROTL(v3,16); |
|
45 list($v[6], $v[7]) = self::rotl_64($v[6], $v[7], 16); |
|
46 |
|
47 # v3 ^= v2; |
|
48 $v[6] ^= $v[4]; |
|
49 $v[7] ^= $v[5]; |
|
50 |
|
51 # v0 += v3; |
|
52 list($v[0], $v[1]) = self::add( |
|
53 array((int) $v[0], (int) $v[1]), |
|
54 array((int) $v[6], (int) $v[7]) |
|
55 ); |
|
56 |
|
57 # v3=ROTL(v3,21); |
|
58 list($v[6], $v[7]) = self::rotl_64((int) $v[6], (int) $v[7], 21); |
|
59 |
|
60 # v3 ^= v0; |
|
61 $v[6] ^= $v[0]; |
|
62 $v[7] ^= $v[1]; |
|
63 |
|
64 # v2 += v1; |
|
65 list($v[4], $v[5]) = self::add( |
|
66 array((int) $v[4], (int) $v[5]), |
|
67 array((int) $v[2], (int) $v[3]) |
|
68 ); |
|
69 |
|
70 # v1=ROTL(v1,17); |
|
71 list($v[2], $v[3]) = self::rotl_64((int) $v[2], (int) $v[3], 17); |
|
72 |
|
73 # v1 ^= v2;; |
|
74 $v[2] ^= $v[4]; |
|
75 $v[3] ^= $v[5]; |
|
76 |
|
77 # v2=ROTL(v2,32) |
|
78 list($v[4], $v[5]) = self::rotl_64((int) $v[4], (int) $v[5], 32); |
|
79 |
|
80 return $v; |
|
81 } |
|
82 |
|
83 /** |
|
84 * Add two 32 bit integers representing a 64-bit integer. |
|
85 * |
|
86 * @internal You should not use this directly from another application |
|
87 * |
|
88 * @param int[] $a |
|
89 * @param int[] $b |
|
90 * @return array<int, mixed> |
|
91 */ |
|
92 public static function add(array $a, array $b) |
|
93 { |
|
94 /** @var int $x1 */ |
|
95 $x1 = $a[1] + $b[1]; |
|
96 /** @var int $c */ |
|
97 $c = $x1 >> 32; // Carry if ($a + $b) > 0xffffffff |
|
98 /** @var int $x0 */ |
|
99 $x0 = $a[0] + $b[0] + $c; |
|
100 return array( |
|
101 $x0 & 0xffffffff, |
|
102 $x1 & 0xffffffff |
|
103 ); |
|
104 } |
|
105 |
|
106 /** |
|
107 * @internal You should not use this directly from another application |
|
108 * |
|
109 * @param int $int0 |
|
110 * @param int $int1 |
|
111 * @param int $c |
|
112 * @return array<int, mixed> |
|
113 */ |
|
114 public static function rotl_64($int0, $int1, $c) |
|
115 { |
|
116 $int0 &= 0xffffffff; |
|
117 $int1 &= 0xffffffff; |
|
118 $c &= 63; |
|
119 if ($c === 32) { |
|
120 return array($int1, $int0); |
|
121 } |
|
122 if ($c > 31) { |
|
123 $tmp = $int1; |
|
124 $int1 = $int0; |
|
125 $int0 = $tmp; |
|
126 $c &= 31; |
|
127 } |
|
128 if ($c === 0) { |
|
129 return array($int0, $int1); |
|
130 } |
|
131 return array( |
|
132 0xffffffff & ( |
|
133 ($int0 << $c) |
|
134 | |
|
135 ($int1 >> (32 - $c)) |
|
136 ), |
|
137 0xffffffff & ( |
|
138 ($int1 << $c) |
|
139 | |
|
140 ($int0 >> (32 - $c)) |
|
141 ), |
|
142 ); |
|
143 } |
|
144 |
|
145 /** |
|
146 * Implements Siphash-2-4 using only 32-bit numbers. |
|
147 * |
|
148 * When we split an int into two, the higher bits go to the lower index. |
|
149 * e.g. 0xDEADBEEFAB10C92D becomes [ |
|
150 * 0 => 0xDEADBEEF, |
|
151 * 1 => 0xAB10C92D |
|
152 * ]. |
|
153 * |
|
154 * @internal You should not use this directly from another application |
|
155 * |
|
156 * @param string $in |
|
157 * @param string $key |
|
158 * @return string |
|
159 * @throws SodiumException |
|
160 * @throws TypeError |
|
161 */ |
|
162 public static function sipHash24($in, $key) |
|
163 { |
|
164 $inlen = self::strlen($in); |
|
165 |
|
166 # /* "somepseudorandomlygeneratedbytes" */ |
|
167 # u64 v0 = 0x736f6d6570736575ULL; |
|
168 # u64 v1 = 0x646f72616e646f6dULL; |
|
169 # u64 v2 = 0x6c7967656e657261ULL; |
|
170 # u64 v3 = 0x7465646279746573ULL; |
|
171 $v = array( |
|
172 0x736f6d65, // 0 |
|
173 0x70736575, // 1 |
|
174 0x646f7261, // 2 |
|
175 0x6e646f6d, // 3 |
|
176 0x6c796765, // 4 |
|
177 0x6e657261, // 5 |
|
178 0x74656462, // 6 |
|
179 0x79746573 // 7 |
|
180 ); |
|
181 // v0 => $v[0], $v[1] |
|
182 // v1 => $v[2], $v[3] |
|
183 // v2 => $v[4], $v[5] |
|
184 // v3 => $v[6], $v[7] |
|
185 |
|
186 # u64 k0 = LOAD64_LE( k ); |
|
187 # u64 k1 = LOAD64_LE( k + 8 ); |
|
188 $k = array( |
|
189 self::load_4(self::substr($key, 4, 4)), |
|
190 self::load_4(self::substr($key, 0, 4)), |
|
191 self::load_4(self::substr($key, 12, 4)), |
|
192 self::load_4(self::substr($key, 8, 4)) |
|
193 ); |
|
194 // k0 => $k[0], $k[1] |
|
195 // k1 => $k[2], $k[3] |
|
196 |
|
197 # b = ( ( u64 )inlen ) << 56; |
|
198 $b = array( |
|
199 $inlen << 24, |
|
200 0 |
|
201 ); |
|
202 // See docblock for why the 0th index gets the higher bits. |
|
203 |
|
204 # v3 ^= k1; |
|
205 $v[6] ^= $k[2]; |
|
206 $v[7] ^= $k[3]; |
|
207 # v2 ^= k0; |
|
208 $v[4] ^= $k[0]; |
|
209 $v[5] ^= $k[1]; |
|
210 # v1 ^= k1; |
|
211 $v[2] ^= $k[2]; |
|
212 $v[3] ^= $k[3]; |
|
213 # v0 ^= k0; |
|
214 $v[0] ^= $k[0]; |
|
215 $v[1] ^= $k[1]; |
|
216 |
|
217 $left = $inlen; |
|
218 # for ( ; in != end; in += 8 ) |
|
219 while ($left >= 8) { |
|
220 # m = LOAD64_LE( in ); |
|
221 $m = array( |
|
222 self::load_4(self::substr($in, 4, 4)), |
|
223 self::load_4(self::substr($in, 0, 4)) |
|
224 ); |
|
225 |
|
226 # v3 ^= m; |
|
227 $v[6] ^= $m[0]; |
|
228 $v[7] ^= $m[1]; |
|
229 |
|
230 # SIPROUND; |
|
231 # SIPROUND; |
|
232 $v = self::sipRound($v); |
|
233 $v = self::sipRound($v); |
|
234 |
|
235 # v0 ^= m; |
|
236 $v[0] ^= $m[0]; |
|
237 $v[1] ^= $m[1]; |
|
238 |
|
239 $in = self::substr($in, 8); |
|
240 $left -= 8; |
|
241 } |
|
242 |
|
243 # switch( left ) |
|
244 # { |
|
245 # case 7: b |= ( ( u64 )in[ 6] ) << 48; |
|
246 # case 6: b |= ( ( u64 )in[ 5] ) << 40; |
|
247 # case 5: b |= ( ( u64 )in[ 4] ) << 32; |
|
248 # case 4: b |= ( ( u64 )in[ 3] ) << 24; |
|
249 # case 3: b |= ( ( u64 )in[ 2] ) << 16; |
|
250 # case 2: b |= ( ( u64 )in[ 1] ) << 8; |
|
251 # case 1: b |= ( ( u64 )in[ 0] ); break; |
|
252 # case 0: break; |
|
253 # } |
|
254 switch ($left) { |
|
255 case 7: |
|
256 $b[0] |= self::chrToInt($in[6]) << 16; |
|
257 case 6: |
|
258 $b[0] |= self::chrToInt($in[5]) << 8; |
|
259 case 5: |
|
260 $b[0] |= self::chrToInt($in[4]); |
|
261 case 4: |
|
262 $b[1] |= self::chrToInt($in[3]) << 24; |
|
263 case 3: |
|
264 $b[1] |= self::chrToInt($in[2]) << 16; |
|
265 case 2: |
|
266 $b[1] |= self::chrToInt($in[1]) << 8; |
|
267 case 1: |
|
268 $b[1] |= self::chrToInt($in[0]); |
|
269 case 0: |
|
270 break; |
|
271 } |
|
272 // See docblock for why the 0th index gets the higher bits. |
|
273 |
|
274 # v3 ^= b; |
|
275 $v[6] ^= $b[0]; |
|
276 $v[7] ^= $b[1]; |
|
277 |
|
278 # SIPROUND; |
|
279 # SIPROUND; |
|
280 $v = self::sipRound($v); |
|
281 $v = self::sipRound($v); |
|
282 |
|
283 # v0 ^= b; |
|
284 $v[0] ^= $b[0]; |
|
285 $v[1] ^= $b[1]; |
|
286 |
|
287 // Flip the lower 8 bits of v2 which is ($v[4], $v[5]) in our implementation |
|
288 # v2 ^= 0xff; |
|
289 $v[5] ^= 0xff; |
|
290 |
|
291 # SIPROUND; |
|
292 # SIPROUND; |
|
293 # SIPROUND; |
|
294 # SIPROUND; |
|
295 $v = self::sipRound($v); |
|
296 $v = self::sipRound($v); |
|
297 $v = self::sipRound($v); |
|
298 $v = self::sipRound($v); |
|
299 |
|
300 # b = v0 ^ v1 ^ v2 ^ v3; |
|
301 # STORE64_LE( out, b ); |
|
302 return self::store32_le($v[1] ^ $v[3] ^ $v[5] ^ $v[7]) . |
|
303 self::store32_le($v[0] ^ $v[2] ^ $v[4] ^ $v[6]); |
|
304 } |
|
305 } |