]> git.meshlink.io Git - meshlink/blob - test/blackbox/run_blackbox_tests/test_cases_key_rotation.c
Refactor the non-blackbox test suite.
[meshlink] / test / blackbox / run_blackbox_tests / test_cases_key_rotation.c
1 /*
2     test_cases_key_rotation.c -- Execution of specific meshlink black box test cases
3     Copyright (C) 2019  Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <assert.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <setjmp.h>
25 #include <cmocka.h>
26 #include <pthread.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <sys/wait.h>
31 #include <signal.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <utime.h>
35 #include "execute_tests.h"
36 #include "test_cases_key_rotation.h"
37 #include "../common/test_step.h"
38 #include "../common/common_handlers.h"
39 #include "../../../src/devtools.h"
40 #include "../../utils.h"
41
42 static void test_case_key_rotation_01(void **state);
43 static bool test_key_rotation_01(void);
44 static void test_case_key_rotation_02(void **state);
45 static bool test_key_rotation_02(void);
46 static void test_case_key_rotation_03(void **state);
47 static bool test_key_rotation_03(void);
48 static void test_case_key_rotation_04(void **state);
49 static bool test_key_rotation_04(void);
50 static void test_case_key_rotation_05(void **state);
51 static bool test_key_rotation_05(void);
52
53 /* Execute key rotation Test Case # 1 - Sanity test */
54 static void test_case_key_rotation_01(void **state) {
55         execute_test(test_key_rotation_01, state);
56 }
57
58 /* Test Steps for key rotation Test Case # 1
59
60     Test Steps:
61     1. Open encrypted node instance, call encrypted rotate API with
62         invalid input parameters to the call.
63
64     Expected Result:
65     Key rotate should fail when called with invalid parameters.
66 */
67 static bool test_key_rotation_01(void) {
68         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
69         meshlink_destroy("encrypted_conf");
70
71         // Open a new meshlink instance.
72
73         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
74         assert_int_not_equal(mesh, NULL);
75
76         // Pass invalid arguments
77
78         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, NULL, 5);
79         assert_int_equal(keyrotation_status, false);
80
81         keyrotation_status = meshlink_encrypted_key_rotate(NULL, "newkey", 6);
82         assert_int_equal(keyrotation_status, false);
83
84         keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 0);
85         assert_int_equal(keyrotation_status, false);
86
87         // Cleanup
88
89         meshlink_close(mesh);
90         meshlink_destroy("encrypted_conf");
91
92         return true;
93 }
94
95 /* Execute key rotation Test Case # 2 - Sanity test */
96 static void test_case_key_rotation_02(void **state) {
97         execute_test(test_key_rotation_02, state);
98 }
99
100 /* Test Steps for key rotation Test Case # 2
101
102     Test Steps:
103     1. Open encrypted node instance, rotate it's key with a newkey and close the node.
104     2. Reopen the encrypted node instance with the newkey
105
106     Expected Result:
107     Opening encrypted node instance should succeed when tried to open with newkey that's
108     been changed to new by key rotate API.
109 */
110 static bool test_key_rotation_02(void) {
111         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
112         meshlink_destroy("encrypted_conf");
113
114         // Open a new meshlink instance.
115
116         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
117         assert_int_not_equal(mesh, NULL);
118         meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
119
120         // Set a new port for the mesh
121
122         int port = 0x1000 + (rand() & 0x7fff);
123         assert_int_equal(meshlink_set_port(mesh, port), true);
124
125         // Key rotate the encrypted_conf storage with new key
126
127         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
128         assert_int_equal(keyrotation_status, true);
129
130         meshlink_close(mesh);
131
132         // Reopen the meshlink instance with the new key
133
134         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
135         assert_int_not_equal(mesh, NULL);
136
137         // Validate the port number that we changed in the last run.
138
139         assert_int_equal(meshlink_get_port(mesh), port);
140
141         // Cleanup
142
143         meshlink_close(mesh);
144         meshlink_destroy("encrypted_conf");
145
146         return true;
147 }
148
149 /* Execute key rotation Test Case # 3 - Sanity test */
150 static void test_case_key_rotation_03(void **state) {
151         execute_test(test_key_rotation_03, state);
152 }
153
154 /* Test Steps for key rotation Test Case # 3
155
156     Test Steps:
157     1. Open encrypted node instance, rotate it's key with a newkey and close the node.
158     2. Reopen the encrypted node instance with the oldkey
159
160     Expected Result:
161     Opening encrypted node instance should fail when tried to open with oldkey that's
162     been changed to new by key rotate API.
163 */
164 static bool test_key_rotation_03(void) {
165         meshlink_destroy("encrypted_conf");
166         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
167
168         // Open a new meshlink instance.
169
170         meshlink_handle_t *mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
171         assert_int_not_equal(mesh, NULL);
172
173         // Key rotate the encrypted_conf storage with new key
174
175         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
176         assert_int_equal(keyrotation_status, true);
177
178         meshlink_close(mesh);
179
180         // Reopen the meshlink instance with the new key
181
182         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
183         assert_int_equal(mesh, NULL);
184
185         // Cleanup
186
187         meshlink_destroy("encrypted_conf");
188
189         return true;
190 }
191
192 /* Execute key rotation Test Case # 4 - Sanity test */
193 static void test_case_key_rotation_04(void **state) {
194         execute_test(test_key_rotation_04, state);
195 }
196
197 /* Test Steps for key rotation Test Case # 4
198     Verify whether key rotation API gracefully handles invitations porting from
199     old key to new key.
200
201     Test Steps:
202     1. Open foo node instance and generate invitations for peer and bar.
203     2. Do key rotation with newkey and verify invitation timestamps post key rotation.
204     3. Change timestamp of peer key to expire and Open instances of foo, bar and peer nodes
205         and try to join bar and peer node.
206
207     Expected Result:
208     Key rotation API should never change the any file status attributes of an invitation file.
209 */
210 static bool test_key_rotation_04(void) {
211         meshlink_handle_t *mesh;
212         meshlink_handle_t *mesh1;
213         meshlink_handle_t *mesh2;
214         struct dirent *ent;
215         DIR *d;
216         char invitation_path_buff[500];
217         struct stat temp_stat;
218         struct stat peer_stat;
219         struct utimbuf timebuf;
220         bool join_status;
221         char *invitations_directory_path = "encrypted_conf/current/invitations/";
222
223         meshlink_destroy("encrypted_conf");
224         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
225
226         // Open a new meshlink instance.
227
228         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
229         assert_int_not_equal(mesh, NULL);
230
231         // Generate invitations
232
233         char *invitation1 = meshlink_invite(mesh, NULL, "peer");
234         assert_int_not_equal(invitation1, NULL);
235
236         // Read the peer invitation file status structure
237
238         strcpy(invitation_path_buff, invitations_directory_path);
239         d = opendir(invitation_path_buff);
240         assert(d);
241
242         while((ent = readdir(d)) != NULL) {
243                 if(ent->d_name[0] == '.') {
244                         continue;
245                 }
246
247                 strcpy(invitation_path_buff, invitations_directory_path);
248                 strcat(invitation_path_buff, ent->d_name);
249                 assert(stat(invitation_path_buff, &temp_stat) != -1);
250
251                 if((temp_stat.st_mode & S_IFMT) == S_IFREG) {
252                         break;
253                 }
254         }
255
256         assert(ent);
257
258         closedir(d);
259
260         char *invitation2 = meshlink_invite(mesh, NULL, "bar");
261         assert_int_not_equal(invitation2, NULL);
262
263         // Key rotate the encrypted_conf storage with new key
264
265         bool keyrotation_status = meshlink_encrypted_key_rotate(mesh, "newkey", 6);
266         assert_int_equal(keyrotation_status, true);
267
268         meshlink_close(mesh);
269
270         // Compare invitation file timestamps of old key with new key
271
272         assert(stat(invitation_path_buff, &peer_stat) != -1);
273         assert_int_equal(peer_stat.st_mtime, temp_stat.st_mtime);
274
275         // Change timestamp for @ peer @ node invitation
276
277         timebuf.actime = peer_stat.st_atime;
278         timebuf.modtime = peer_stat.st_mtime - 604805; // > 1 week
279
280         assert(utime(invitation_path_buff, &timebuf) != -1);
281
282
283         // Reopen the meshlink instance with the new key
284
285         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
286         assert_int_not_equal(mesh, NULL);
287
288         mesh1 = meshlink_open("encrypted_conf.1", "peer", "encrypted", DEV_CLASS_BACKBONE);
289         assert_int_not_equal(mesh1, NULL);
290
291         mesh2 = meshlink_open("encrypted_conf.2", "bar", "encrypted", DEV_CLASS_BACKBONE);
292         assert_int_not_equal(mesh2, NULL);
293
294         assert(meshlink_start(mesh));
295
296         join_status = meshlink_join(mesh1, invitation1);
297         assert_int_equal(join_status, false);
298
299         join_status = meshlink_join(mesh2, invitation2);
300         assert_int_equal(join_status, true);
301
302         // Cleanup
303
304         free(invitation1);
305         free(invitation2);
306         meshlink_close(mesh);
307         meshlink_close(mesh1);
308         meshlink_close(mesh2);
309         meshlink_destroy("encrypted_conf");
310         meshlink_destroy("encrypted_conf.1");
311         meshlink_destroy("encrypted_conf.2");
312
313         return true;
314 }
315
316 /* Execute key rotation Test Case # 5 - Atomicity test */
317 static void test_case_key_rotation_05(void **state) {
318         execute_test(test_key_rotation_05, state);
319 }
320
321 static int break_stage;
322
323 static void nop_stage(int stage) {
324         (void)stage;
325
326         return;
327 }
328
329 static void debug_probe(int stage) {
330
331         // Terminate the node at the specified stage (by @ break_stage @ )
332         if(stage == break_stage) {
333                 raise(SIGINT);
334         } else if((break_stage < 1) || (break_stage > 3)) {
335                 fprintf(stderr, "INVALID stage break\n");
336                 raise(SIGABRT);
337         }
338
339         return;
340 }
341
342 /* Test Steps for key rotation Test Case # 5
343     Debug all stages of key rotate API and verify it's atomicity
344
345     Test Steps:
346     1. Open foo node instance.
347     2. In a loop break meshlink node instance at each stage incrementally
348         in a fork process
349     3. Reopen node instance post termination.
350
351     Expected Result:
352     Terminating node instance when meshlink_encrypted_key_rotate function called
353     at any stage should give atomic result when reopened.
354 */
355 static bool test_key_rotation_05(void) {
356         pid_t pid;
357         int status;
358         meshlink_handle_t *mesh;
359         meshlink_destroy("encrypted_conf");
360         meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb);
361
362         assert(signal(SIGINT, SIG_DFL) != SIG_ERR);
363         assert(signal(SIGABRT, SIG_DFL) != SIG_ERR);
364
365         // Set debug_probe callback
366
367         devtool_keyrotate_probe = debug_probe;
368         int new_port = 12000;
369         int pipefd[2];
370
371         // incrementally debug meshlink_encrypted_key_rotate API atomicity
372
373         for(break_stage = 1; break_stage <= 3; break_stage += 1) {
374                 fprintf(stderr, "Debugging stage %d\n", break_stage);
375                 meshlink_destroy("encrypted_conf");
376
377                 assert(pipe(pipefd) != -1);
378
379                 pid = fork();
380                 assert(pid != -1);
381
382                 if(!pid) {
383                         close(pipefd[0]);
384                         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
385                         assert(mesh);
386                         meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
387                         meshlink_enable_discovery(mesh, false);
388
389                         assert(meshlink_set_port(mesh, new_port));
390
391                         char *invitation = meshlink_invite(mesh, NULL, "bar");
392                         assert(invitation);
393
394                         assert(write(pipefd[1], invitation, strlen(invitation) + 1) != -1);
395
396                         meshlink_encrypted_key_rotate(mesh, "newkey", 6);
397                         raise(SIGABRT);
398                 }
399
400                 close(pipefd[1]);
401
402                 // Wait for child exit and verify which signal terminated it
403
404                 assert(waitpid(pid, &status, 0) != -1);
405                 assert_int_equal(WIFSIGNALED(status), true);
406                 assert_int_equal(WTERMSIG(status), SIGINT);
407
408                 // Reopen the node with invalid key other than old and new key should fail and should not affect
409                 // the existing confbase
410
411                 fprintf(stderr, "Opening mesh with invalid key\n");
412                 mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "invalidkey", 9);
413                 assert_int_equal(mesh, NULL);
414
415                 // Reopen the node with the "newkey", if it failed to open with "newkey" then
416                 // opening with the "oldkey" should succeed
417
418                 fprintf(stderr, "Opening mesh with new-key\n");
419                 mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "newkey", 6);
420
421                 if(!mesh) {
422                         fprintf(stderr, "Opening mesh with new-key failed trying to open with old-key\n");
423                         mesh = meshlink_open_encrypted("encrypted_conf", "foo", "encrypted", DEV_CLASS_BACKBONE, "oldkey", 6);
424                         assert_int_not_equal(mesh, NULL);
425                 }
426
427                 meshlink_set_log_cb(mesh, MESHLINK_DEBUG, log_cb);
428                 meshlink_enable_discovery(mesh, false);
429
430                 // Verify the newly set port and generated invitation
431
432                 int get_port = meshlink_get_port(mesh);
433                 assert_int_equal(get_port, new_port);
434
435                 char invitation[200];
436                 assert(read(pipefd[0], invitation, sizeof(invitation)) != -1);
437
438                 assert(meshlink_start(mesh));
439
440                 meshlink_destroy("encrypted_conf.1");
441
442                 meshlink_handle_t *mesh2 = meshlink_open("encrypted_conf.1", "bar", "bar", DEV_CLASS_BACKBONE);
443                 assert(mesh2);
444
445                 meshlink_set_log_cb(mesh2, MESHLINK_DEBUG, log_cb);
446                 meshlink_enable_discovery(mesh2, false);
447
448                 assert_int_equal(meshlink_join(mesh2, invitation), true);
449
450                 // cleanup
451
452                 meshlink_close(mesh);
453                 meshlink_close(mesh2);
454
455                 close(pipefd[0]);
456         }
457
458         // Cleanup
459
460         meshlink_destroy("encrypted_conf");
461         meshlink_destroy("encrypted_conf.1");
462         devtool_keyrotate_probe = nop_stage;
463         return true;
464 }
465
466 int test_meshlink_encrypted_key_rotation(void) {
467         /* State structures for key rotation Test Cases */
468         black_box_state_t test_case_key_rotation_01_state = {
469                 .test_case_name = "test_case_key_rotation_01",
470         };
471         black_box_state_t test_case_key_rotation_02_state = {
472                 .test_case_name = "test_case_key_rotation_02",
473         };
474         black_box_state_t test_case_key_rotation_03_state = {
475                 .test_case_name = "test_case_key_rotation_03",
476         };
477         black_box_state_t test_case_key_rotation_04_state = {
478                 .test_case_name = "test_case_key_rotation_04",
479         };
480         black_box_state_t test_case_key_rotation_05_state = {
481                 .test_case_name = "test_case_key_rotation_05",
482         };
483
484         const struct CMUnitTest blackbox_status_tests[] = {
485                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_01, NULL, NULL,
486                                 (void *)&test_case_key_rotation_01_state),
487                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_02, NULL, NULL,
488                                 (void *)&test_case_key_rotation_02_state),
489                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_03, NULL, NULL,
490                                 (void *)&test_case_key_rotation_03_state),
491                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_04, NULL, NULL,
492                                 (void *)&test_case_key_rotation_04_state),
493                 cmocka_unit_test_prestate_setup_teardown(test_case_key_rotation_05, NULL, NULL,
494                                 (void *)&test_case_key_rotation_05_state),
495         };
496         total_tests += sizeof(blackbox_status_tests) / sizeof(blackbox_status_tests[0]);
497
498         return cmocka_run_group_tests(blackbox_status_tests, NULL, NULL);
499 }