1<?php
2
3if (class_exists('ParagonIE_Sodium_Core_AEGIS_State256', false)) {
4 return;
5}
6
7if (!defined('SODIUM_COMPAT_AEGIS_C0')) {
8 define('SODIUM_COMPAT_AEGIS_C0', "\x00\x01\x01\x02\x03\x05\x08\x0d\x15\x22\x37\x59\x90\xe9\x79\x62");
9}
10if (!defined('SODIUM_COMPAT_AEGIS_C1')) {
11 define('SODIUM_COMPAT_AEGIS_C1', "\xdb\x3d\x18\x55\x6d\xc2\x2f\xf1\x20\x11\x31\x42\x73\xb5\x28\xdd");
12}
13
14class ParagonIE_Sodium_Core_AEGIS_State256
15{
16 /** @var array<int, string> $state */
17 protected $state;
18 public function __construct()
19 {
20 $this->state = array_fill(0, 6, '');
21 }
22
23 /**
24 * @internal Only use this for unit tests!
25 * @return string[]
26 */
27 public function getState()
28 {
29 return array_values($this->state);
30 }
31
32 /**
33 * @param array $input
34 * @return self
35 * @throws SodiumException
36 *
37 * @internal Only for unit tests
38 */
39 public static function initForUnitTests(array $input)
40 {
41 if (count($input) < 6) {
42 throw new SodiumException('invalid input');
43 }
44 $state = new self();
45 for ($i = 0; $i < 6; ++$i) {
46 $state->state[$i] = $input[$i];
47 }
48 return $state;
49 }
50
51 /**
52 * @param string $key
53 * @param string $nonce
54 * @return self
55 */
56 public static function init($key, $nonce)
57 {
58 $state = new self();
59 $k0 = ParagonIE_Sodium_Core_Util::substr($key, 0, 16);
60 $k1 = ParagonIE_Sodium_Core_Util::substr($key, 16, 16);
61 $n0 = ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16);
62 $n1 = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 16);
63
64 // S0 = k0 ^ n0
65 // S1 = k1 ^ n1
66 // S2 = C1
67 // S3 = C0
68 // S4 = k0 ^ C0
69 // S5 = k1 ^ C1
70 $k0_n0 = $k0 ^ $n0;
71 $k1_n1 = $k1 ^ $n1;
72 $state->state[0] = $k0_n0;
73 $state->state[1] = $k1_n1;
74 $state->state[2] = SODIUM_COMPAT_AEGIS_C1;
75 $state->state[3] = SODIUM_COMPAT_AEGIS_C0;
76 $state->state[4] = $k0 ^ SODIUM_COMPAT_AEGIS_C0;
77 $state->state[5] = $k1 ^ SODIUM_COMPAT_AEGIS_C1;
78
79 // Repeat(4,
80 // Update(k0)
81 // Update(k1)
82 // Update(k0 ^ n0)
83 // Update(k1 ^ n1)
84 // )
85 for ($i = 0; $i < 4; ++$i) {
86 $state->update($k0);
87 $state->update($k1);
88 $state->update($k0 ^ $n0);
89 $state->update($k1 ^ $n1);
90 }
91 return $state;
92 }
93
94 /**
95 * @param string $ai
96 * @return self
97 * @throws SodiumException
98 */
99 public function absorb($ai)
100 {
101 if (ParagonIE_Sodium_Core_Util::strlen($ai) !== 16) {
102 throw new SodiumException('Input must be an AES block in size');
103 }
104 return $this->update($ai);
105 }
106
107 /**
108 * @param string $ci
109 * @return string
110 * @throws SodiumException
111 */
112 public function dec($ci)
113 {
114 if (ParagonIE_Sodium_Core_Util::strlen($ci) !== 16) {
115 throw new SodiumException('Input must be an AES block in size');
116 }
117 // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
118 $z = $this->state[1]
119 ^ $this->state[4]
120 ^ $this->state[5]
121 ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
122 $xi = $ci ^ $z;
123 $this->update($xi);
124 return $xi;
125 }
126
127 /**
128 * @param string $cn
129 * @return string
130 */
131 public function decPartial($cn)
132 {
133 $len = ParagonIE_Sodium_Core_Util::strlen($cn);
134 // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
135 $z = $this->state[1]
136 ^ $this->state[4]
137 ^ $this->state[5]
138 ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
139
140 // t = ZeroPad(cn, 128)
141 $t = str_pad($cn, 16, "\0", STR_PAD_RIGHT);
142
143 // out = t ^ z
144 $out = $t ^ $z;
145
146 // xn = Truncate(out, |cn|)
147 $xn = ParagonIE_Sodium_Core_Util::substr($out, 0, $len);
148
149 // v = ZeroPad(xn, 128)
150 $v = str_pad($xn, 16, "\0", STR_PAD_RIGHT);
151 // Update(v)
152 $this->update($v);
153
154 // return xn
155 return $xn;
156 }
157
158 /**
159 * @param string $xi
160 * @return string
161 * @throws SodiumException
162 */
163 public function enc($xi)
164 {
165 if (ParagonIE_Sodium_Core_Util::strlen($xi) !== 16) {
166 throw new SodiumException('Input must be an AES block in size');
167 }
168 // z = S1 ^ S4 ^ S5 ^ (S2 & S3)
169 $z = $this->state[1]
170 ^ $this->state[4]
171 ^ $this->state[5]
172 ^ ParagonIE_Sodium_Core_Util::andStrings($this->state[2], $this->state[3]);
173 $this->update($xi);
174 return $xi ^ $z;
175 }
176
177 /**
178 * @param int $ad_len_bits
179 * @param int $msg_len_bits
180 * @return string
181 */
182 public function finalize($ad_len_bits, $msg_len_bits)
183 {
184 $encoded = ParagonIE_Sodium_Core_Util::store64_le($ad_len_bits) .
185 ParagonIE_Sodium_Core_Util::store64_le($msg_len_bits);
186 $t = $this->state[3] ^ $encoded;
187
188 for ($i = 0; $i < 7; ++$i) {
189 $this->update($t);
190 }
191
192 return ($this->state[0] ^ $this->state[1] ^ $this->state[2]) .
193 ($this->state[3] ^ $this->state[4] ^ $this->state[5]);
194 }
195
196 /**
197 * @param string $m
198 * @return self
199 */
200 public function update($m)
201 {
202 /*
203 S'0 = AESRound(S5, S0 ^ M)
204 S'1 = AESRound(S0, S1)
205 S'2 = AESRound(S1, S2)
206 S'3 = AESRound(S2, S3)
207 S'4 = AESRound(S3, S4)
208 S'5 = AESRound(S4, S5)
209 */
210 list($s_0, $s_1) = ParagonIE_Sodium_Core_AES::doubleRound(
211 $this->state[5],$this->state[0] ^ $m,
212 $this->state[0], $this->state[1]
213 );
214
215 list($s_2, $s_3) = ParagonIE_Sodium_Core_AES::doubleRound(
216 $this->state[1], $this->state[2],
217 $this->state[2], $this->state[3]
218 );
219 list($s_4, $s_5) = ParagonIE_Sodium_Core_AES::doubleRound(
220 $this->state[3], $this->state[4],
221 $this->state[4], $this->state[5]
222 );
223
224 /*
225 S0 = S'0
226 S1 = S'1
227 S2 = S'2
228 S3 = S'3
229 S4 = S'4
230 S5 = S'5
231 */
232 $this->state[0] = $s_0;
233 $this->state[1] = $s_1;
234 $this->state[2] = $s_2;
235 $this->state[3] = $s_3;
236 $this->state[4] = $s_4;
237 $this->state[5] = $s_5;
238 return $this;
239 }
240}
241