Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 24... Línea 24...
24
 * @package core
24
 * @package core
25
 * @copyright 2020 The Open University
25
 * @copyright 2020 The Open University
26
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27
 * @covers  \core\encryption
27
 * @covers  \core\encryption
28
 */
28
 */
29
class encryption_test extends advanced_testcase {
29
final class encryption_test extends advanced_testcase {
30
 
-
 
31
    /**
-
 
32
     * Clear junk created by tests.
-
 
33
     */
-
 
34
    protected function tearDown(): void {
-
 
35
        global $CFG;
-
 
36
        $keyfile = encryption::get_key_file(encryption::METHOD_OPENSSL);
-
 
37
        if (file_exists($keyfile)) {
-
 
38
            chmod($keyfile, 0700);
-
 
39
        }
-
 
40
        $keyfile = encryption::get_key_file(encryption::METHOD_SODIUM);
-
 
41
        if (file_exists($keyfile)) {
-
 
42
            chmod($keyfile, 0700);
-
 
43
        }
-
 
44
        remove_dir($CFG->dataroot . '/secret');
-
 
45
        unset($CFG->nokeygeneration);
-
 
46
    }
-
 
Línea 47... Línea 30...
47
 
30
 
48
    protected function setUp(): void {
31
    protected function setUp(): void {
49
        $this->tearDown();
-
 
50
 
32
        parent::setUp();
51
        require_once(__DIR__ . '/fixtures/testable_encryption.php');
33
        require_once(__DIR__ . '/fixtures/testable_encryption.php');
Línea 52... Línea 34...
52
    }
34
    }
53
 
35
 
54
    /**
36
    /**
55
     * Many of the tests work with both encryption methods.
37
     * Return a list of supported encryption methods
56
     *
38
     *
57
     * @return array[] Array of method options for test
39
     * @return array[] Array of method options for test
58
     */
40
     */
59
    public function encryption_method_provider(): array {
41
    public static function encryption_method_provider(): array {
60
        return [
42
        return [
61
            'Sodium' => [encryption::METHOD_SODIUM],
43
            'Sodium' => [encryption::METHOD_SODIUM],
Línea 76... Línea 58...
76
        $this->expectExceptionMessage('Key already exists');
58
        $this->expectExceptionMessage('Key already exists');
77
        encryption::create_key($method);
59
        encryption::create_key($method);
78
    }
60
    }
Línea 79... Línea 61...
79
 
61
 
80
    /**
-
 
81
     * Test that we can create keys for legacy {@see encryption::METHOD_OPENSSL} content
-
 
82
     */
-
 
83
    public function test_create_key_openssl(): void {
-
 
84
        encryption::create_key(encryption::METHOD_OPENSSL);
-
 
85
        $key = testable_encryption::get_key(encryption::METHOD_OPENSSL);
-
 
86
        $this->assertEquals(32, strlen($key));
-
 
87
 
-
 
88
        $this->expectExceptionMessage('Key already exists');
-
 
89
        encryption::create_key(encryption::METHOD_OPENSSL);
-
 
90
    }
-
 
91
 
-
 
92
    /**
62
    /**
93
     * Tests encryption and decryption with empty strings.
63
     * Tests encryption and decryption with empty strings.
94
     */
64
     */
95
    public function test_encrypt_and_decrypt_empty(): void {
65
    public function test_encrypt_and_decrypt_empty(): void {
96
        $this->assertEquals('', encryption::encrypt(''));
66
        $this->assertEquals('', encryption::encrypt(''));
Línea 111... Línea 81...
111
        $this->expectExceptionMessage('Key not found');
81
        $this->expectExceptionMessage('Key not found');
112
        encryption::encrypt('frogs', $method);
82
        encryption::encrypt('frogs', $method);
113
    }
83
    }
Línea 114... Línea 84...
114
 
84
 
115
    /**
-
 
116
     * Test that attempting to encrypt with legacy {@see encryption::METHOD_OPENSSL} method falls back to Sodium
-
 
117
     */
-
 
118
    public function test_encrypt_openssl(): void {
-
 
119
        $encrypted = encryption::encrypt('Frogs', encryption::METHOD_OPENSSL);
-
 
120
        $this->assertStringStartsWith(encryption::METHOD_SODIUM . ':', $encrypted);
-
 
121
        $this->assertDebuggingCalledCount(1, ['Encryption using legacy OpenSSL is deprecated, reverting to Sodium']);
-
 
122
    }
-
 
123
 
-
 
124
    /**
85
    /**
125
     * Tests decryption when the data has a different encryption method
86
     * Tests decryption when the data has a different encryption method
126
     */
87
     */
127
    public function test_decrypt_wrongmethod(): void {
88
    public function test_decrypt_wrongmethod(): void {
128
        $this->expectExceptionMessage('Data does not match a supported encryption method');
89
        $this->expectExceptionMessage('Data does not match a supported encryption method');
Línea 134... Línea 95...
134
     *
95
     *
135
     * @dataProvider encryption_method_provider
96
     * @dataProvider encryption_method_provider
136
     * @param string $method Encryption method
97
     * @param string $method Encryption method
137
     */
98
     */
138
    public function test_decrypt_tooshort(string $method): void {
99
    public function test_decrypt_tooshort(string $method): void {
139
 
-
 
140
        $this->expectExceptionMessage('Insufficient data');
-
 
141
        switch ($method) {
100
        switch ($method) {
142
            case encryption::METHOD_OPENSSL:
-
 
143
                // It needs min 49 bytes (16 bytes IV + 32 bytes HMAC + 1 byte data).
-
 
144
                $justtooshort = '0123456789abcdef0123456789abcdef0123456789abcdef';
-
 
145
                break;
-
 
146
            case encryption::METHOD_SODIUM:
101
            case encryption::METHOD_SODIUM:
147
                // Sodium needs 25 bytes at least as far as our code is concerned (24 bytes IV + 1
102
                // Sodium needs 25 bytes at least as far as our code is concerned (24 bytes IV + 1
148
                // byte data); it splits out any authentication hashes itself.
103
                // byte data); it splits out any authentication hashes itself.
149
                $justtooshort = '0123456789abcdef01234567';
104
                $justtooshort = '0123456789abcdef01234567';
150
                break;
105
                break;
151
        }
106
        }
Línea -... Línea 107...
-
 
107
 
152
 
108
        $this->expectExceptionMessage('Insufficient data');
153
        encryption::decrypt($method . ':' .base64_encode($justtooshort));
109
        encryption::decrypt($method . ':' .base64_encode($justtooshort));
Línea 154... Línea 110...
154
    }
110
    }
155
 
111
 
Línea 179... Línea 135...
179
        encryption::decrypt($method . ':' . base64_encode(
135
        encryption::decrypt($method . ':' . base64_encode(
180
                '0123456789abcdef0123456789abcdef0123456789abcdef0'));
136
                '0123456789abcdef0123456789abcdef0123456789abcdef0'));
181
    }
137
    }
Línea 182... Línea 138...
182
 
138
 
183
    /**
-
 
184
     * Test that we can decrypt legacy {@see encryption::METHOD_OPENSSL} content
-
 
185
     */
-
 
186
    public function test_decrypt_openssl(): void {
-
 
187
        $key = testable_encryption::get_key(encryption::METHOD_OPENSSL);
-
 
188
 
-
 
189
        // Construct encrypted string using openssl method/cipher.
-
 
190
        $iv = random_bytes(openssl_cipher_iv_length(encryption::OPENSSL_CIPHER));
-
 
191
        $encrypted = @openssl_encrypt('Frogs', encryption::OPENSSL_CIPHER, $key, OPENSSL_RAW_DATA, $iv);
-
 
192
        $hmac = hash_hmac('sha256', $iv . $encrypted, $key, true);
-
 
193
 
-
 
194
        $decrypted = encryption::decrypt(encryption::METHOD_OPENSSL . ':' . base64_encode($iv . $encrypted . $hmac));
-
 
195
        $this->assertEquals('Frogs', $decrypted);
-
 
196
        $this->assertDebuggingCalledCount(1, ['Decryption using legacy OpenSSL is deprecated, please upgrade to Sodium']);
-
 
197
    }
-
 
198
 
-
 
199
    /**
139
    /**
200
     * Test automatic generation of keys when needed.
140
     * Test automatic generation of keys when needed.
201
     *
141
     *
202
     * @dataProvider encryption_method_provider
142
     * @dataProvider encryption_method_provider
203
     * @param string $method Encryption method
143
     * @param string $method Encryption method
Línea 225... Línea 165...
225
 
165
 
226
        switch ($method) {
166
        switch ($method) {
227
            case encryption::METHOD_SODIUM:
167
            case encryption::METHOD_SODIUM:
228
                $this->expectExceptionMessageMatches('/(should|must) be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes/');
168
                $this->expectExceptionMessageMatches('/(should|must) be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes/');
229
                break;
-
 
230
 
-
 
231
            case encryption::METHOD_OPENSSL:
-
 
232
                $this->expectExceptionMessage('Invalid key');
-
 
233
                break;
169
                break;
-
 
170
        }
234
        }
171
 
235
        encryption::encrypt('frogs', $method);
172
        encryption::encrypt('frogs', $method);
Línea 236... Línea 173...
236
    }
173
    }
237
 
174
 
238
    /**
175
    /**
239
     * Checks that modified data causes failures.
176
     * Checks that modified data causes failures.
240
     *
177
     *
241
     * @dataProvider encryption_method_provider
178
     * @dataProvider encryption_method_provider
242
     * @param string $method Encryption method
179
     * @param string $method Encryption method
243
     */
-
 
244
    public function test_modified_data(string $method): void {
180
     */
245
 
181
    public function test_modified_data(string $method): void {
246
        $encrypted = encryption::encrypt('frogs', $method);
182
        $encrypted = encryption::encrypt('frogs', $method);
247
        $mainbit = base64_decode(substr($encrypted, strlen($method) + 1));
183
        $mainbit = base64_decode(substr($encrypted, strlen($method) + 1));
248
        $mainbit = substr($mainbit, 0, 16) . 'X' . substr($mainbit, 16);
184
        $mainbit = substr($mainbit, 0, 16) . 'X' . substr($mainbit, 16);