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
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace core;
18
 
19
use advanced_testcase;
20
 
21
/**
22
 * Test encryption.
23
 *
24
 * @package core
25
 * @copyright 2020 The Open University
26
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27
 * @covers  \core\encryption
28
 */
1441 ariadna 29
final class encryption_test extends advanced_testcase {
1 efrain 30
 
31
    protected function setUp(): void {
1441 ariadna 32
        parent::setUp();
1 efrain 33
        require_once(__DIR__ . '/fixtures/testable_encryption.php');
34
    }
35
 
36
    /**
1441 ariadna 37
     * Return a list of supported encryption methods
1 efrain 38
     *
39
     * @return array[] Array of method options for test
40
     */
1441 ariadna 41
    public static function encryption_method_provider(): array {
1 efrain 42
        return [
43
            'Sodium' => [encryption::METHOD_SODIUM],
44
        ];
45
    }
46
 
47
    /**
48
     * Tests the create_keys and get_key functions.
49
     *
50
     * @param string $method Encryption method
51
     * @dataProvider encryption_method_provider
52
     */
53
    public function test_create_key(string $method): void {
54
        encryption::create_key($method);
55
        $key = testable_encryption::get_key($method);
56
        $this->assertEquals(32, strlen($key));
57
 
58
        $this->expectExceptionMessage('Key already exists');
59
        encryption::create_key($method);
60
    }
61
 
62
    /**
63
     * Tests encryption and decryption with empty strings.
64
     */
65
    public function test_encrypt_and_decrypt_empty(): void {
66
        $this->assertEquals('', encryption::encrypt(''));
67
        $this->assertEquals('', encryption::decrypt(''));
68
    }
69
 
70
    /**
71
     * Tests encryption when the keys weren't created yet.
72
     *
73
     * @param string $method Encryption method
74
     * @dataProvider encryption_method_provider
75
     */
76
    public function test_encrypt_nokeys(string $method): void {
77
        global $CFG;
78
 
79
        // Prevent automatic generation of keys.
80
        $CFG->nokeygeneration = true;
81
        $this->expectExceptionMessage('Key not found');
82
        encryption::encrypt('frogs', $method);
83
    }
84
 
85
    /**
86
     * Tests decryption when the data has a different encryption method
87
     */
88
    public function test_decrypt_wrongmethod(): void {
89
        $this->expectExceptionMessage('Data does not match a supported encryption method');
90
        encryption::decrypt('FAKE-CIPHER-METHOD:xx');
91
    }
92
 
93
    /**
94
     * Tests decryption when not enough data is supplied to get the IV and some data.
95
     *
96
     * @dataProvider encryption_method_provider
97
     * @param string $method Encryption method
98
     */
99
    public function test_decrypt_tooshort(string $method): void {
100
        switch ($method) {
101
            case encryption::METHOD_SODIUM:
102
                // Sodium needs 25 bytes at least as far as our code is concerned (24 bytes IV + 1
103
                // byte data); it splits out any authentication hashes itself.
104
                $justtooshort = '0123456789abcdef01234567';
105
                break;
106
        }
107
 
1441 ariadna 108
        $this->expectExceptionMessage('Insufficient data');
1 efrain 109
        encryption::decrypt($method . ':' .base64_encode($justtooshort));
110
    }
111
 
112
    /**
113
     * Tests decryption when data is not valid base64.
114
     *
115
     * @dataProvider encryption_method_provider
116
     * @param string $method Encryption method
117
     */
118
    public function test_decrypt_notbase64(string $method): void {
119
        $this->expectExceptionMessage('Invalid base64 data');
120
        encryption::decrypt($method . ':' . chr(160));
121
    }
122
 
123
    /**
124
     * Tests decryption when the keys weren't created yet.
125
     *
126
     * @dataProvider encryption_method_provider
127
     * @param string $method Encryption method
128
     */
129
    public function test_decrypt_nokeys(string $method): void {
130
        global $CFG;
131
 
132
        // Prevent automatic generation of keys.
133
        $CFG->nokeygeneration = true;
134
        $this->expectExceptionMessage('Key not found');
135
        encryption::decrypt($method . ':' . base64_encode(
136
                '0123456789abcdef0123456789abcdef0123456789abcdef0'));
137
    }
138
 
139
    /**
140
     * Test automatic generation of keys when needed.
141
     *
142
     * @dataProvider encryption_method_provider
143
     * @param string $method Encryption method
144
     */
145
    public function test_auto_key_generation(string $method): void {
146
 
147
        // Allow automatic generation (default).
148
        $encrypted = encryption::encrypt('frogs', $method);
149
        $this->assertEquals('frogs', encryption::decrypt($encrypted));
150
    }
151
 
152
    /**
153
     * Checks that invalid key causes failures.
154
     *
155
     * @dataProvider encryption_method_provider
156
     * @param string $method Encryption method
157
     */
158
    public function test_invalid_key(string $method): void {
159
        global $CFG;
160
 
161
        // Set the key to something bogus.
162
        $folder = $CFG->dataroot . '/secret/key';
163
        check_dir_exists($folder);
164
        file_put_contents(encryption::get_key_file($method), 'silly');
165
 
166
        switch ($method) {
167
            case encryption::METHOD_SODIUM:
168
                $this->expectExceptionMessageMatches('/(should|must) be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes/');
169
                break;
1441 ariadna 170
        }
1 efrain 171
 
172
        encryption::encrypt('frogs', $method);
173
    }
174
 
175
    /**
176
     * Checks that modified data causes failures.
177
     *
178
     * @dataProvider encryption_method_provider
179
     * @param string $method Encryption method
180
     */
181
    public function test_modified_data(string $method): void {
182
        $encrypted = encryption::encrypt('frogs', $method);
183
        $mainbit = base64_decode(substr($encrypted, strlen($method) + 1));
184
        $mainbit = substr($mainbit, 0, 16) . 'X' . substr($mainbit, 16);
185
        $encrypted = $method . ':' . base64_encode($mainbit);
186
        $this->expectExceptionMessage('Integrity check failed');
187
        encryption::decrypt($encrypted);
188
    }
189
 
190
    /**
191
     * Tests encryption and decryption for real.
192
     *
193
     * @dataProvider encryption_method_provider
194
     * @param string $method Encryption method
195
     */
196
    public function test_encrypt_and_decrypt_realdata(string $method): void {
197
 
198
        // Encrypt short string.
199
        $encrypted = encryption::encrypt('frogs', $method);
200
        $this->assertNotEquals('frogs', $encrypted);
201
        $this->assertEquals('frogs', encryption::decrypt($encrypted));
202
 
203
        // Encrypt really long string (1 MB).
204
        $long = str_repeat('X', 1024 * 1024);
205
        $this->assertEquals($long, encryption::decrypt(encryption::encrypt($long, $method)));
206
    }
207
}