Proyectos de Subversion Moodle

Rev

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