PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / trunk
Transferito: WP Migration vtrunk
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / vendor / aws / aws-sdk-php / src / Crypto / Polyfill / Gmac.php
transferito / vendor / aws / aws-sdk-php / src / Crypto / Polyfill Last commit date
AesGcm.php 11 months ago ByteArray.php 11 months ago Gmac.php 11 months ago Key.php 11 months ago NeedsTrait.php 11 months ago
Gmac.php
224 lines
1 <?php
2 namespace Aws\Crypto\Polyfill;
3
4 /**
5 * Class Gmac
6 *
7 * @package Aws\Crypto\Polyfill
8 */
9 class Gmac
10 {
11 use NeedsTrait;
12
13 const BLOCK_SIZE = 16;
14
15 /** @var ByteArray $buf */
16 protected $buf;
17
18 /** @var int $bufLength */
19 protected $bufLength = 0;
20
21 /** @var ByteArray $h */
22 protected $h;
23
24 /** @var ByteArray $hf */
25 protected $hf;
26
27 /** @var Key $key */
28 protected $key;
29
30 /** @var ByteArray $x */
31 protected $x;
32
33 /**
34 * Gmac constructor.
35 *
36 * @param Key $aesKey
37 * @param string $nonce
38 * @param int $keySize
39 */
40 public function __construct(Key $aesKey, $nonce, $keySize = 256)
41 {
42 $this->buf = new ByteArray(16);
43 $this->h = new ByteArray(
44 \openssl_encrypt(
45 \str_repeat("\0", 16),
46 "aes-{$keySize}-ecb",
47 $aesKey->get(),
48 OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
49 )
50 );
51 $this->key = $aesKey;
52 $this->x = new ByteArray(16);
53 $this->hf = new ByteArray(
54 \openssl_encrypt(
55 $nonce,
56 "aes-{$keySize}-ecb",
57 $aesKey->get(),
58 OPENSSL_RAW_DATA | OPENSSL_NO_PADDING
59 )
60 );
61 }
62
63 /**
64 * Update the object with some data.
65 *
66 * This method mutates this Gmac object.
67 *
68 * @param ByteArray $blocks
69 * @return self
70 */
71 public function update(ByteArray $blocks)
72 {
73 if (($blocks->count() + $this->bufLength) < self::BLOCK_SIZE) {
74 // Write to internal buffer until we reach enough to write.
75 $this->buf->set($blocks, $this->bufLength);
76 $this->bufLength += $blocks->count();
77 return $this;
78 }
79
80 // Process internal buffer first.
81 if ($this->bufLength > 0) {
82 // 0 <= state.buf_len < BLOCK_SIZE is an invariant
83 $tmp = new ByteArray(self::BLOCK_SIZE);
84 $tmp->set($this->buf->slice(0, $this->bufLength));
85 $remainingBlockLength = self::BLOCK_SIZE - $this->bufLength;
86 $tmp->set($blocks->slice(0, $remainingBlockLength), $this->bufLength);
87 $blocks = $blocks->slice($remainingBlockLength);
88 $this->bufLength = 0;
89 $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
90 }
91
92 // Process full blocks.
93 $numBlocks = $blocks->count() >> 4;
94 for ($i = 0; $i < $numBlocks; ++$i) {
95 $tmp = $blocks->slice($i << 4, self::BLOCK_SIZE);
96 $this->x = $this->blockMultiply($this->x->exclusiveOr($tmp), $this->h);
97 }
98 $last = $numBlocks << 4;
99
100 // Zero-fill buffer
101 for ($i = 0; $i < 16; ++$i) {
102 $this->buf[$i] = 0;
103 }
104 // Feed leftover into buffer.
105 if ($last < $blocks->count()) {
106 $tmp = $blocks->slice($last);
107 $this->buf->set($tmp);
108 $this->bufLength += ($blocks->count() - $last);
109 }
110 return $this;
111 }
112
113 /**
114 * Finish processing the authentication tag.
115 *
116 * This method mutates this Gmac object (effectively resetting it).
117 *
118 * @param int $aadLength
119 * @param int $ciphertextLength
120 * @return ByteArray
121 */
122 public function finish($aadLength, $ciphertextLength)
123 {
124 $lengthBlock = new ByteArray(16);
125 $state = $this->flush();
126
127 // AES-GCM expects bit lengths, not byte lengths.
128 $lengthBlock->set(ByteArray::enc32be($aadLength >> 29), 0);
129 $lengthBlock->set(ByteArray::enc32be($aadLength << 3), 4);
130 $lengthBlock->set(ByteArray::enc32be($ciphertextLength >> 29), 8);
131 $lengthBlock->set(ByteArray::enc32be($ciphertextLength << 3), 12);
132
133 $state->update($lengthBlock);
134 $output = $state->x->exclusiveOr($state->hf);
135
136 // Zeroize the internal values as a best-effort.
137 $state->buf->zeroize();
138 $state->x->zeroize();
139 $state->h->zeroize();
140 $state->hf->zeroize();
141 return $output;
142 }
143
144 /**
145 * Get a specific bit from the provided array, at the given index.
146 *
147 * [01234567], 8+[01234567], 16+[01234567], ...
148 *
149 * @param ByteArray $x
150 * @param int $i
151 * @return int
152 */
153 protected function bit(ByteArray $x, $i)
154 {
155 $byte = $i >> 3;
156 return ($x[$byte] >> ((7 - $i) & 7)) & 1;
157 }
158
159 /**
160 * Galois Field Multiplication
161 *
162 * This function is the critical path that must be constant-time in order to
163 * avoid timing side-channels against AES-GCM.
164 *
165 * The contents of each are always calculated, regardless of the branching
166 * condition, to prevent another kind of timing leak.
167 *
168 * @param ByteArray $x
169 * @param ByteArray $y
170 * @return ByteArray
171 */
172 protected function blockMultiply(ByteArray $x, ByteArray $y)
173 {
174 static $fieldPolynomial = null;
175 if (!$fieldPolynomial) {
176 $fieldPolynomial = new ByteArray([
177 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
178 ]);
179 }
180 self::needs($x->count() === 16, 'Argument 1 must be a ByteArray of exactly 16 bytes');
181 self::needs($y->count() === 16, 'Argument 2 must be a ByteArray of exactly 16 bytes');
182
183 $v = clone $y;
184 $z = new ByteArray(16);
185
186 for ($i = 0; $i < 128; ++$i) {
187 // if ($b) $z = $z->exclusiveOr($v);
188 $b = $this->bit($x, $i);
189 $z = ByteArray::select(
190 $b,
191 $z->exclusiveOr($v),
192 $z
193 );
194
195 // if ($b) $v = $v->exclusiveOr($fieldPolynomial);
196 $b = $v[15] & 1;
197 $v = $v->rshift();
198 $v = ByteArray::select(
199 $b,
200 $v->exclusiveOr($fieldPolynomial),
201 $v
202 );
203 }
204 return $z;
205 }
206
207 /**
208 * Finish processing any leftover bytes in the internal buffer.
209 *
210 * @return self
211 */
212 public function flush()
213 {
214 if ($this->bufLength !== 0) {
215 $this->x = $this->blockMultiply(
216 $this->x->exclusiveOr($this->buf),
217 $this->h
218 );
219 $this->bufLength = 0;
220 }
221 return $this;
222 }
223 }
224