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