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
use Aws\Exception\CryptoPolyfillException;
5
use InvalidArgumentException;
6
use RangeException;
7
 
8
/**
9
 * Class AesGcm
10
 *
11
 * This provides a polyfill for AES-GCM encryption/decryption, with caveats:
12
 *
13
 * 1. Only 96-bit nonces are supported.
14
 * 2. Only 128-bit authentication tags are supported. (i.e. non-truncated)
15
 *
16
 * Supports AES key sizes of 128-bit, 192-bit, and 256-bit.
17
 *
18
 * @package Aws\Crypto\Polyfill
19
 */
20
class AesGcm
21
{
22
    use NeedsTrait;
23
 
24
    /** @var Key $aesKey */
25
    private $aesKey;
26
 
27
    /** @var int $keySize */
28
    private $keySize;
29
 
30
    /** @var int $blockSize */
31
    protected $blockSize = 8192;
32
 
33
    /**
34
     * AesGcm constructor.
35
     *
36
     * @param Key $aesKey
37
     * @param int $keySize
38
     * @param int $blockSize
39
     *
40
     * @throws CryptoPolyfillException
41
     * @throws InvalidArgumentException
42
     * @throws RangeException
43
     */
44
    public function __construct(Key $aesKey, $keySize = 256, $blockSize = 8192)
45
    {
46
        /* Preconditions: */
47
        self::needs(
48
            \in_array($keySize, [128, 192, 256], true),
49
            "Key size must be 128, 192, or 256 bits; {$keySize} given",
50
            InvalidArgumentException::class
51
        );
52
        self::needs(
53
            \is_int($blockSize) && $blockSize > 0 && $blockSize <= PHP_INT_MAX,
54
            'Block size must be a positive integer.',
55
            RangeException::class
56
        );
57
        self::needs(
58
            $aesKey->length() << 3 === $keySize,
59
            'Incorrect key size; expected ' . $keySize . ' bits, got ' . ($aesKey->length() << 3) . ' bits.'
60
        );
61
        $this->aesKey = $aesKey;
62
        $this->keySize = $keySize;
63
    }
64
 
65
    /**
66
     * Encryption interface for AES-GCM
67
     *
68
     * @param string $plaintext  Message to be encrypted
69
     * @param string $nonce      Number to be used ONCE
70
     * @param Key $key           AES Key
71
     * @param string $aad        Additional authenticated data
72
     * @param string &$tag       Reference to variable to hold tag
73
     * @param int $keySize       Key size (bits)
74
     * @param int $blockSize     Block size (bytes) -- How much memory to buffer
75
     * @return string
76
     * @throws InvalidArgumentException
77
     */
78
    public static function encrypt(
79
        $plaintext,
80
        $nonce,
81
        Key $key,
82
        $aad,
83
        &$tag,
84
        $keySize = 256,
85
        $blockSize = 8192
86
    ) {
87
        self::needs(
88
            self::strlen($nonce) === 12,
89
            'Nonce must be exactly 12 bytes',
90
            InvalidArgumentException::class
91
        );
92
 
93
        $encryptor = new AesGcm($key, $keySize, $blockSize);
94
        list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
95
 
96
        $ciphertext = \openssl_encrypt(
97
            $plaintext,
98
            "aes-{$encryptor->keySize}-ctr",
99
            $key->get(),
100
            OPENSSL_NO_PADDING | OPENSSL_RAW_DATA,
101
            $nonce . "\x00\x00\x00\x02"
102
        );
103
 
104
        /* Calculate auth tag in a streaming fashion to minimize memory usage: */
105
        $ciphertextLength = self::strlen($ciphertext);
106
        for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
107
            $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
108
            $gmac->update($cBlock);
109
        }
110
        $tag = $gmac->finish($aadLength, $ciphertextLength)->toString();
111
        return $ciphertext;
112
    }
113
 
114
    /**
115
     * Decryption interface for AES-GCM
116
     *
117
     * @param string $ciphertext Ciphertext to decrypt
118
     * @param string $nonce      Number to be used ONCE
119
     * @param Key $key           AES key
120
     * @param string $aad        Additional authenticated data
121
     * @param string $tag        Authentication tag
122
     * @param int $keySize       Key size (bits)
123
     * @param int $blockSize     Block size (bytes) -- How much memory to buffer
124
     * @return string            Plaintext
125
     *
126
     * @throws CryptoPolyfillException
127
     * @throws InvalidArgumentException
128
     */
129
    public static function decrypt(
130
        $ciphertext,
131
        $nonce,
132
        Key $key,
133
        $aad,
134
        &$tag,
135
        $keySize = 256,
136
        $blockSize = 8192
137
    ) {
138
        /* Precondition: */
139
        self::needs(
140
            self::strlen($nonce) === 12,
141
            'Nonce must be exactly 12 bytes',
142
            InvalidArgumentException::class
143
        );
144
 
145
        $encryptor = new AesGcm($key, $keySize, $blockSize);
146
        list($aadLength, $gmac) = $encryptor->gmacInit($nonce, $aad);
147
 
148
        /* Calculate auth tag in a streaming fashion to minimize memory usage: */
149
        $ciphertextLength = self::strlen($ciphertext);
150
        for ($i = 0; $i < $ciphertextLength; $i += $encryptor->blockSize) {
151
            $cBlock = new ByteArray(self::substr($ciphertext, $i, $encryptor->blockSize));
152
            $gmac->update($cBlock);
153
        }
154
 
155
        /* Validate auth tag in constant-time: */
156
        $calc = $gmac->finish($aadLength, $ciphertextLength);
157
        $expected = new ByteArray($tag);
158
        self::needs($calc->equals($expected), 'Invalid authentication tag');
159
 
160
        /* Return plaintext if auth tag check succeeded: */
161
        return \openssl_decrypt(
162
            $ciphertext,
163
            "aes-{$encryptor->keySize}-ctr",
164
            $key->get(),
165
            OPENSSL_NO_PADDING | OPENSSL_RAW_DATA,
166
            $nonce . "\x00\x00\x00\x02"
167
        );
168
    }
169
 
170
    /**
171
     * Initialize a Gmac object with the nonce and this object's key.
172
     *
173
     * @param string $nonce     Must be exactly 12 bytes long.
174
     * @param string|null $aad
175
     * @return array
176
     */
177
    protected function gmacInit($nonce, $aad = null)
178
    {
179
        $gmac = new Gmac(
180
            $this->aesKey,
181
            $nonce . "\x00\x00\x00\x01",
182
            $this->keySize
183
        );
184
        $aadBlock = new ByteArray($aad);
185
        $aadLength = $aadBlock->count();
186
        $gmac->update($aadBlock);
187
        $gmac->flush();
188
        return [$aadLength, $gmac];
189
    }
190
 
191
    /**
192
     * Calculate the length of a string.
193
     *
194
     * Uses the appropriate PHP function without being brittle to
195
     * mbstring.func_overload.
196
     *
197
     * @param string $string
198
     * @return int
199
     */
200
    protected static function strlen($string)
201
    {
202
        if (\is_callable('\\mb_strlen')) {
203
            return (int) \mb_strlen($string, '8bit');
204
        }
205
        return (int) \strlen($string);
206
    }
207
 
208
    /**
209
     * Return a substring of the provided string.
210
     *
211
     * Uses the appropriate PHP function without being brittle to
212
     * mbstring.func_overload.
213
     *
214
     * @param string $string
215
     * @param int $offset
216
     * @param int|null $length
217
     * @return string
218
     */
219
    protected static function substr($string, $offset = 0, $length = null)
220
    {
221
        if (\is_callable('\\mb_substr')) {
222
            return \mb_substr($string, $offset, $length, '8bit');
223
        } elseif (!\is_null($length)) {
224
            return \substr($string, $offset, $length);
225
        }
226
        return \substr($string, $offset);
227
    }
228
}