1<?php
2
3if (class_exists('ParagonIE_Sodium_Core32_Poly1305_State', false)) {
4 return;
5}
6
7/**
8 * Class ParagonIE_Sodium_Core32_Poly1305_State
9 */
10class ParagonIE_Sodium_Core32_Poly1305_State extends ParagonIE_Sodium_Core32_Util
11{
12 /**
13 * @var array<int, int>
14 */
15 protected $buffer = array();
16
17 /**
18 * @var bool
19 */
20 protected $final = false;
21
22 /**
23 * @var array<int, ParagonIE_Sodium_Core32_Int32>
24 */
25 public $h;
26
27 /**
28 * @var int
29 */
30 protected $leftover = 0;
31
32 /**
33 * @var array<int, ParagonIE_Sodium_Core32_Int32>
34 */
35 public $r;
36
37 /**
38 * @var array<int, ParagonIE_Sodium_Core32_Int64>
39 */
40 public $pad;
41
42 /**
43 * ParagonIE_Sodium_Core32_Poly1305_State constructor.
44 *
45 * @internal You should not use this directly from another application
46 *
47 * @param string $key
48 * @throws InvalidArgumentException
49 * @throws SodiumException
50 * @throws TypeError
51 */
52 public function __construct($key = '')
53 {
54 if (self::strlen($key) < 32) {
55 throw new InvalidArgumentException(
56 'Poly1305 requires a 32-byte key'
57 );
58 }
59 /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
60 $this->r = array(
61 // st->r[0] = ...
62 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 0, 4))
63 ->setUnsignedInt(true)
64 ->mask(0x3ffffff),
65 // st->r[1] = ...
66 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 3, 4))
67 ->setUnsignedInt(true)
68 ->shiftRight(2)
69 ->mask(0x3ffff03),
70 // st->r[2] = ...
71 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 6, 4))
72 ->setUnsignedInt(true)
73 ->shiftRight(4)
74 ->mask(0x3ffc0ff),
75 // st->r[3] = ...
76 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 9, 4))
77 ->setUnsignedInt(true)
78 ->shiftRight(6)
79 ->mask(0x3f03fff),
80 // st->r[4] = ...
81 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 12, 4))
82 ->setUnsignedInt(true)
83 ->shiftRight(8)
84 ->mask(0x00fffff)
85 );
86
87 /* h = 0 */
88 $this->h = array(
89 new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
90 new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
91 new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
92 new ParagonIE_Sodium_Core32_Int32(array(0, 0), true),
93 new ParagonIE_Sodium_Core32_Int32(array(0, 0), true)
94 );
95
96 /* save pad for later */
97 $this->pad = array(
98 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 16, 4))
99 ->setUnsignedInt(true)->toInt64(),
100 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 20, 4))
101 ->setUnsignedInt(true)->toInt64(),
102 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 24, 4))
103 ->setUnsignedInt(true)->toInt64(),
104 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($key, 28, 4))
105 ->setUnsignedInt(true)->toInt64(),
106 );
107
108 $this->leftover = 0;
109 $this->final = false;
110 }
111
112 /**
113 * @internal You should not use this directly from another application
114 *
115 * @param string $message
116 * @return self
117 * @throws SodiumException
118 * @throws TypeError
119 */
120 public function update($message = '')
121 {
122 $bytes = self::strlen($message);
123
124 /* handle leftover */
125 if ($this->leftover) {
126 /** @var int $want */
127 $want = ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE - $this->leftover;
128 if ($want > $bytes) {
129 $want = $bytes;
130 }
131 for ($i = 0; $i < $want; ++$i) {
132 $mi = self::chrToInt($message[$i]);
133 $this->buffer[$this->leftover + $i] = $mi;
134 }
135 // We snip off the leftmost bytes.
136 $message = self::substr($message, $want);
137 $bytes = self::strlen($message);
138 $this->leftover += $want;
139 if ($this->leftover < ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
140 // We still don't have enough to run $this->blocks()
141 return $this;
142 }
143
144 $this->blocks(
145 self::intArrayToString($this->buffer),
146 ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
147 );
148 $this->leftover = 0;
149 }
150
151 /* process full blocks */
152 if ($bytes >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
153 /** @var int $want */
154 $want = $bytes & ~(ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE - 1);
155 if ($want >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
156 /** @var string $block */
157 $block = self::substr($message, 0, $want);
158 if (self::strlen($block) >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
159 $this->blocks($block, $want);
160 $message = self::substr($message, $want);
161 $bytes = self::strlen($message);
162 }
163 }
164 }
165
166 /* store leftover */
167 if ($bytes) {
168 for ($i = 0; $i < $bytes; ++$i) {
169 $mi = self::chrToInt($message[$i]);
170 $this->buffer[$this->leftover + $i] = $mi;
171 }
172 $this->leftover = (int) $this->leftover + $bytes;
173 }
174 return $this;
175 }
176
177 /**
178 * @internal You should not use this directly from another application
179 *
180 * @param string $message
181 * @param int $bytes
182 * @return self
183 * @throws SodiumException
184 * @throws TypeError
185 */
186 public function blocks($message, $bytes)
187 {
188 if (self::strlen($message) < 16) {
189 $message = str_pad($message, 16, "\x00", STR_PAD_RIGHT);
190 }
191 $hibit = ParagonIE_Sodium_Core32_Int32::fromInt((int) ($this->final ? 0 : 1 << 24)); /* 1 << 128 */
192 $hibit->setUnsignedInt(true);
193 $zero = new ParagonIE_Sodium_Core32_Int64(array(0, 0, 0, 0), true);
194 /**
195 * @var ParagonIE_Sodium_Core32_Int64 $d0
196 * @var ParagonIE_Sodium_Core32_Int64 $d1
197 * @var ParagonIE_Sodium_Core32_Int64 $d2
198 * @var ParagonIE_Sodium_Core32_Int64 $d3
199 * @var ParagonIE_Sodium_Core32_Int64 $d4
200 * @var ParagonIE_Sodium_Core32_Int64 $r0
201 * @var ParagonIE_Sodium_Core32_Int64 $r1
202 * @var ParagonIE_Sodium_Core32_Int64 $r2
203 * @var ParagonIE_Sodium_Core32_Int64 $r3
204 * @var ParagonIE_Sodium_Core32_Int64 $r4
205 *
206 * @var ParagonIE_Sodium_Core32_Int32 $h0
207 * @var ParagonIE_Sodium_Core32_Int32 $h1
208 * @var ParagonIE_Sodium_Core32_Int32 $h2
209 * @var ParagonIE_Sodium_Core32_Int32 $h3
210 * @var ParagonIE_Sodium_Core32_Int32 $h4
211 */
212 $r0 = $this->r[0]->toInt64();
213 $r1 = $this->r[1]->toInt64();
214 $r2 = $this->r[2]->toInt64();
215 $r3 = $this->r[3]->toInt64();
216 $r4 = $this->r[4]->toInt64();
217
218 $s1 = $r1->toInt64()->mulInt(5, 3);
219 $s2 = $r2->toInt64()->mulInt(5, 3);
220 $s3 = $r3->toInt64()->mulInt(5, 3);
221 $s4 = $r4->toInt64()->mulInt(5, 3);
222
223 $h0 = $this->h[0];
224 $h1 = $this->h[1];
225 $h2 = $this->h[2];
226 $h3 = $this->h[3];
227 $h4 = $this->h[4];
228
229 while ($bytes >= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE) {
230 /* h += m[i] */
231 $h0 = $h0->addInt32(
232 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 0, 4))
233 ->mask(0x3ffffff)
234 )->toInt64();
235 $h1 = $h1->addInt32(
236 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 3, 4))
237 ->shiftRight(2)
238 ->mask(0x3ffffff)
239 )->toInt64();
240 $h2 = $h2->addInt32(
241 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 6, 4))
242 ->shiftRight(4)
243 ->mask(0x3ffffff)
244 )->toInt64();
245 $h3 = $h3->addInt32(
246 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 9, 4))
247 ->shiftRight(6)
248 ->mask(0x3ffffff)
249 )->toInt64();
250 $h4 = $h4->addInt32(
251 ParagonIE_Sodium_Core32_Int32::fromReverseString(self::substr($message, 12, 4))
252 ->shiftRight(8)
253 ->orInt32($hibit)
254 )->toInt64();
255
256 /* h *= r */
257 $d0 = $zero
258 ->addInt64($h0->mulInt64($r0, 27))
259 ->addInt64($s4->mulInt64($h1, 27))
260 ->addInt64($s3->mulInt64($h2, 27))
261 ->addInt64($s2->mulInt64($h3, 27))
262 ->addInt64($s1->mulInt64($h4, 27));
263
264 $d1 = $zero
265 ->addInt64($h0->mulInt64($r1, 27))
266 ->addInt64($h1->mulInt64($r0, 27))
267 ->addInt64($s4->mulInt64($h2, 27))
268 ->addInt64($s3->mulInt64($h3, 27))
269 ->addInt64($s2->mulInt64($h4, 27));
270
271 $d2 = $zero
272 ->addInt64($h0->mulInt64($r2, 27))
273 ->addInt64($h1->mulInt64($r1, 27))
274 ->addInt64($h2->mulInt64($r0, 27))
275 ->addInt64($s4->mulInt64($h3, 27))
276 ->addInt64($s3->mulInt64($h4, 27));
277
278 $d3 = $zero
279 ->addInt64($h0->mulInt64($r3, 27))
280 ->addInt64($h1->mulInt64($r2, 27))
281 ->addInt64($h2->mulInt64($r1, 27))
282 ->addInt64($h3->mulInt64($r0, 27))
283 ->addInt64($s4->mulInt64($h4, 27));
284
285 $d4 = $zero
286 ->addInt64($h0->mulInt64($r4, 27))
287 ->addInt64($h1->mulInt64($r3, 27))
288 ->addInt64($h2->mulInt64($r2, 27))
289 ->addInt64($h3->mulInt64($r1, 27))
290 ->addInt64($h4->mulInt64($r0, 27));
291
292 /* (partial) h %= p */
293 $c = $d0->shiftRight(26);
294 $h0 = $d0->toInt32()->mask(0x3ffffff);
295 $d1 = $d1->addInt64($c);
296
297 $c = $d1->shiftRight(26);
298 $h1 = $d1->toInt32()->mask(0x3ffffff);
299 $d2 = $d2->addInt64($c);
300
301 $c = $d2->shiftRight(26);
302 $h2 = $d2->toInt32()->mask(0x3ffffff);
303 $d3 = $d3->addInt64($c);
304
305 $c = $d3->shiftRight(26);
306 $h3 = $d3->toInt32()->mask(0x3ffffff);
307 $d4 = $d4->addInt64($c);
308
309 $c = $d4->shiftRight(26);
310 $h4 = $d4->toInt32()->mask(0x3ffffff);
311 $h0 = $h0->addInt32($c->toInt32()->mulInt(5, 3));
312
313 $c = $h0->shiftRight(26);
314 $h0 = $h0->mask(0x3ffffff);
315 $h1 = $h1->addInt32($c);
316
317 // Chop off the left 32 bytes.
318 $message = self::substr(
319 $message,
320 ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
321 );
322 $bytes -= ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE;
323 }
324
325 /** @var array<int, ParagonIE_Sodium_Core32_Int32> $h */
326 $this->h = array($h0, $h1, $h2, $h3, $h4);
327 return $this;
328 }
329
330 /**
331 * @internal You should not use this directly from another application
332 *
333 * @return string
334 * @throws SodiumException
335 * @throws TypeError
336 */
337 public function finish()
338 {
339 /* process the remaining block */
340 if ($this->leftover) {
341 $i = $this->leftover;
342 $this->buffer[$i++] = 1;
343 for (; $i < ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE; ++$i) {
344 $this->buffer[$i] = 0;
345 }
346 $this->final = true;
347 $this->blocks(
348 self::substr(
349 self::intArrayToString($this->buffer),
350 0,
351 ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
352 ),
353 $b = ParagonIE_Sodium_Core32_Poly1305::BLOCK_SIZE
354 );
355 }
356
357 /**
358 * @var ParagonIE_Sodium_Core32_Int32 $f
359 * @var ParagonIE_Sodium_Core32_Int32 $g0
360 * @var ParagonIE_Sodium_Core32_Int32 $g1
361 * @var ParagonIE_Sodium_Core32_Int32 $g2
362 * @var ParagonIE_Sodium_Core32_Int32 $g3
363 * @var ParagonIE_Sodium_Core32_Int32 $g4
364 * @var ParagonIE_Sodium_Core32_Int32 $h0
365 * @var ParagonIE_Sodium_Core32_Int32 $h1
366 * @var ParagonIE_Sodium_Core32_Int32 $h2
367 * @var ParagonIE_Sodium_Core32_Int32 $h3
368 * @var ParagonIE_Sodium_Core32_Int32 $h4
369 */
370 $h0 = $this->h[0];
371 $h1 = $this->h[1];
372 $h2 = $this->h[2];
373 $h3 = $this->h[3];
374 $h4 = $this->h[4];
375
376 $c = $h1->shiftRight(26); # $c = $h1 >> 26;
377 $h1 = $h1->mask(0x3ffffff); # $h1 &= 0x3ffffff;
378
379 $h2 = $h2->addInt32($c); # $h2 += $c;
380 $c = $h2->shiftRight(26); # $c = $h2 >> 26;
381 $h2 = $h2->mask(0x3ffffff); # $h2 &= 0x3ffffff;
382
383 $h3 = $h3->addInt32($c); # $h3 += $c;
384 $c = $h3->shiftRight(26); # $c = $h3 >> 26;
385 $h3 = $h3->mask(0x3ffffff); # $h3 &= 0x3ffffff;
386
387 $h4 = $h4->addInt32($c); # $h4 += $c;
388 $c = $h4->shiftRight(26); # $c = $h4 >> 26;
389 $h4 = $h4->mask(0x3ffffff); # $h4 &= 0x3ffffff;
390
391 $h0 = $h0->addInt32($c->mulInt(5, 3)); # $h0 += self::mul($c, 5);
392 $c = $h0->shiftRight(26); # $c = $h0 >> 26;
393 $h0 = $h0->mask(0x3ffffff); # $h0 &= 0x3ffffff;
394 $h1 = $h1->addInt32($c); # $h1 += $c;
395
396 /* compute h + -p */
397 $g0 = $h0->addInt(5);
398 $c = $g0->shiftRight(26);
399 $g0 = $g0->mask(0x3ffffff);
400 $g1 = $h1->addInt32($c);
401 $c = $g1->shiftRight(26);
402 $g1 = $g1->mask(0x3ffffff);
403 $g2 = $h2->addInt32($c);
404 $c = $g2->shiftRight(26);
405 $g2 = $g2->mask(0x3ffffff);
406 $g3 = $h3->addInt32($c);
407 $c = $g3->shiftRight(26);
408 $g3 = $g3->mask(0x3ffffff);
409 $g4 = $h4->addInt32($c)->subInt(1 << 26);
410
411 # $mask = ($g4 >> 31) - 1;
412 /* select h if h < p, or h + -p if h >= p */
413 $mask = (int) (($g4->toInt() >> 31) + 1);
414
415 $g0 = $g0->mask($mask);
416 $g1 = $g1->mask($mask);
417 $g2 = $g2->mask($mask);
418 $g3 = $g3->mask($mask);
419 $g4 = $g4->mask($mask);
420
421 /** @var int $mask */
422 $mask = ~$mask;
423
424 $h0 = $h0->mask($mask)->orInt32($g0);
425 $h1 = $h1->mask($mask)->orInt32($g1);
426 $h2 = $h2->mask($mask)->orInt32($g2);
427 $h3 = $h3->mask($mask)->orInt32($g3);
428 $h4 = $h4->mask($mask)->orInt32($g4);
429
430 /* h = h % (2^128) */
431 $h0 = $h0->orInt32($h1->shiftLeft(26));
432 $h1 = $h1->shiftRight(6)->orInt32($h2->shiftLeft(20));
433 $h2 = $h2->shiftRight(12)->orInt32($h3->shiftLeft(14));
434 $h3 = $h3->shiftRight(18)->orInt32($h4->shiftLeft(8));
435
436 /* mac = (h + pad) % (2^128) */
437 $f = $h0->toInt64()->addInt64($this->pad[0]);
438 $h0 = $f->toInt32();
439 $f = $h1->toInt64()->addInt64($this->pad[1])->addInt($h0->overflow);
440 $h1 = $f->toInt32();
441 $f = $h2->toInt64()->addInt64($this->pad[2])->addInt($h1->overflow);
442 $h2 = $f->toInt32();
443 $f = $h3->toInt64()->addInt64($this->pad[3])->addInt($h2->overflow);
444 $h3 = $f->toInt32();
445
446 return $h0->toReverseString() .
447 $h1->toReverseString() .
448 $h2->toReverseString() .
449 $h3->toReverseString();
450 }
451}
452