Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}