1 /*
2  * Copyright (c) 2018-2019 [Ribose Inc](https://www.ribose.com).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *
11  * 2.  Redistributions in binary form must reproduce the above copyright notice,
12  *     this list of conditions and the following disclaimer in the documentation
13  *     and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "rnp_tests.h"
28 #include "support.h"
29 #include "time-utils.h"
30 
31 #ifdef HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #else
34 #ifndef WIFEXITED
35 #define WIFEXITED(stat) (((*((int *) &(stat))) & 0xC0000000) == 0)
36 #endif
37 
38 #ifndef WEXITSTATUS
39 #define WEXITSTATUS(stat) (*((int *) &(stat)))
40 #endif
41 #endif
42 
43 #ifdef _WIN32
44 #include <windows.h>
45 #include "str-utils.h"
46 #endif
47 
48 int rnp_main(int argc, char **argv);
49 int rnpkeys_main(int argc, char **argv);
50 
51 static int
call_rnp(const char * cmd,...)52 call_rnp(const char *cmd, ...)
53 {
54     int     argc = 0;
55     int     res;
56     char ** argv = (char **) calloc(32, sizeof(char *));
57     va_list args;
58 
59     va_start(args, cmd);
60     while (cmd) {
61         argv[argc++] = (char *) cmd;
62         cmd = va_arg(args, char *);
63     }
64     va_end(args);
65     /* reset state of getopt_long used in rnp */
66     optind = 1;
67 
68     if (!strcmp(argv[0], "rnp")) {
69         res = rnp_main(argc, argv);
70     } else if (!strcmp(argv[0], "rnpkeys")) {
71         res = rnpkeys_main(argc, argv);
72     } else {
73         res = -1;
74     }
75     free(argv);
76 
77     return res;
78 }
79 
80 #define KEYS "data/keyrings"
81 #define GENKEYS "data/keyrings_genkey_tmp"
82 #define MKEYS "data/test_stream_key_merge/"
83 #define FILES "data/test_cli"
84 #define G10KEYS "data/test_stream_key_load/g10"
85 
TEST_F(rnp_tests,test_cli_rnp_keyfile)86 TEST_F(rnp_tests, test_cli_rnp_keyfile)
87 {
88     int ret;
89 
90     /* sign with keyfile, using default key */
91     ret = call_rnp("rnp",
92                    "--keyfile",
93                    MKEYS "key-sec.asc",
94                    "--password",
95                    "password",
96                    "-s",
97                    FILES "/hello.txt",
98                    NULL);
99     assert_int_equal(ret, 0);
100     assert_true(rnp_file_exists(FILES "/hello.txt.pgp"));
101     /* verify signed file */
102     ret =
103       call_rnp("rnp", "--keyfile", MKEYS "key-pub.asc", "-v", FILES "/hello.txt.pgp", NULL);
104     assert_int_equal(ret, 0);
105     assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
106 
107     /* sign with keyfile, using user id */
108     ret = call_rnp("rnp",
109                    "-f",
110                    MKEYS "key-sec.asc",
111                    "-u",
112                    "key-merge-uid-2",
113                    "--password",
114                    "password",
115                    "--armor",
116                    "-s",
117                    FILES "/hello.txt",
118                    NULL);
119     assert_int_equal(ret, 0);
120     assert_true(rnp_file_exists(FILES "/hello.txt.asc"));
121     /* verify signed file */
122     ret = call_rnp("rnp", "-f", MKEYS "key-pub.asc", "-v", FILES "/hello.txt.asc", NULL);
123     assert_int_equal(ret, 0);
124     /* verify with key without self-signature - should fail */
125     ret =
126       call_rnp("rnp", "-f", MKEYS "key-pub-just-key.pgp", "-v", FILES "/hello.txt.asc", NULL);
127     assert_int_not_equal(ret, 0);
128     assert_int_equal(rnp_unlink(FILES "/hello.txt.asc"), 0);
129 
130     /* encrypt with keyfile, using default key */
131     ret = call_rnp("rnp", "--keyfile", MKEYS "key-pub.asc", "-e", FILES "/hello.txt", NULL);
132     assert_int_equal(ret, 0);
133     assert_true(rnp_file_exists(FILES "/hello.txt.pgp"));
134     /* decrypt it with raw seckey, without userids and sigs */
135     ret = call_rnp("rnp",
136                    "--keyfile",
137                    MKEYS "key-sec-no-uid-no-sigs.pgp",
138                    "--password",
139                    "password",
140                    "-d",
141                    FILES "/hello.txt.pgp",
142                    NULL);
143     assert_int_equal(ret, 0);
144     assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
145 
146     /* try to encrypt with keyfile, using the signing subkey */
147     ret = call_rnp("rnp",
148                    "--keyfile",
149                    MKEYS "key-pub.asc",
150                    "-r",
151                    "16CD16F267CCDD4F",
152                    "--armor",
153                    "-e",
154                    FILES "/hello.txt",
155                    NULL);
156     assert_int_not_equal(ret, 0);
157     assert_false(rnp_file_exists(FILES "/hello.txt.asc"));
158     /* now encrypt with keyfile, using the encrypting subkey */
159     ret = call_rnp("rnp",
160                    "--keyfile",
161                    MKEYS "key-pub.asc",
162                    "-r",
163                    "AF1114A47F5F5B28",
164                    "--armor",
165                    "-e",
166                    FILES "/hello.txt",
167                    NULL);
168     assert_int_equal(ret, 0);
169     assert_true(rnp_file_exists(FILES "/hello.txt.asc"));
170     /* fail to decrypt it with pubkey */
171     ret = call_rnp("rnp",
172                    "--keyfile",
173                    MKEYS "key-pub-subkey-1.pgp",
174                    "--password",
175                    "password",
176                    "-d",
177                    FILES "/hello.txt.asc",
178                    NULL);
179     assert_int_not_equal(ret, 0);
180     /* decrypt correctly with seckey + subkeys */
181     ret = call_rnp("rnp",
182                    "--keyfile",
183                    MKEYS "key-sec.pgp",
184                    "--password",
185                    "password",
186                    "-d",
187                    FILES "/hello.txt.asc",
188                    NULL);
189     assert_int_equal(ret, 0);
190     assert_int_equal(rnp_unlink(FILES "/hello.txt.asc"), 0);
191 }
192 
193 static bool
test_cli_g10_key_sign(const char * userid)194 test_cli_g10_key_sign(const char *userid)
195 {
196     int ret;
197 
198     /* create signature */
199     ret = call_rnp("rnp",
200                    "--homedir",
201                    G10KEYS,
202                    "--password",
203                    "password",
204                    "-u",
205                    userid,
206                    "-s",
207                    FILES "/hello.txt",
208                    NULL);
209     if (ret) {
210         return false;
211     }
212 
213     /* verify back */
214     ret = call_rnp("rnp", "--homedir", G10KEYS, "-v", FILES "/hello.txt.pgp", NULL);
215     if (ret) {
216         return false;
217     }
218     rnp_unlink(FILES "/hello.txt.pgp");
219     return true;
220 }
221 
222 static bool
test_cli_g10_key_encrypt(const char * userid)223 test_cli_g10_key_encrypt(const char *userid)
224 {
225     int ret;
226 
227     /* encrypt */
228     ret = call_rnp("rnp", "--homedir", G10KEYS, "-r", userid, "-e", FILES "/hello.txt", NULL);
229     if (ret) {
230         return false;
231     }
232 
233     /* decrypt it back */
234     ret = call_rnp("rnp",
235                    "--homedir",
236                    G10KEYS,
237                    "--password",
238                    "password",
239                    "-d",
240                    FILES "/hello.txt.pgp",
241                    NULL);
242     if (ret) {
243         return false;
244     }
245     rnp_unlink(FILES "/hello.txt.pgp");
246     return true;
247 }
248 
TEST_F(rnp_tests,test_cli_g10_operations)249 TEST_F(rnp_tests, test_cli_g10_operations)
250 {
251     int ret;
252 
253     /* sign with default g10 key */
254     ret = call_rnp(
255       "rnp", "--homedir", G10KEYS, "--password", "password", "-s", FILES "/hello.txt", NULL);
256     assert_int_equal(ret, 0);
257 
258     /* verify back */
259     ret = call_rnp("rnp", "--homedir", G10KEYS, "-v", FILES "/hello.txt.pgp", NULL);
260     assert_int_equal(ret, 0);
261     assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
262 
263     /* encrypt with default g10 key */
264     ret = call_rnp("rnp", "--homedir", G10KEYS, "-e", FILES "/hello.txt", NULL);
265     assert_int_equal(ret, 0);
266 
267     /* decrypt it back */
268     ret = call_rnp("rnp",
269                    "--homedir",
270                    G10KEYS,
271                    "--password",
272                    "password",
273                    "-d",
274                    FILES "/hello.txt.pgp",
275                    NULL);
276     assert_int_equal(ret, 0);
277     assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
278 
279     /* check dsa/eg key */
280     assert_true(test_cli_g10_key_sign("c8a10a7d78273e10"));    // signing key
281     assert_true(test_cli_g10_key_encrypt("c8a10a7d78273e10")); // will find subkey
282     assert_false(test_cli_g10_key_sign("02a5715c3537717e"));   // fail - encrypting subkey
283     assert_true(test_cli_g10_key_encrypt("02a5715c3537717e")); // success
284 
285     /* check rsa/rsa key, key is SC while subkey is E */
286     assert_true(test_cli_g10_key_sign("2fb9179118898e8b"));
287     assert_true(test_cli_g10_key_encrypt("2fb9179118898e8b"));
288     assert_false(test_cli_g10_key_sign("6e2f73008f8b8d6e"));
289     assert_true(test_cli_g10_key_encrypt("6e2f73008f8b8d6e"));
290 
291     /* check ed25519 key */
292     assert_true(test_cli_g10_key_sign("cc786278981b0728"));
293     assert_false(test_cli_g10_key_encrypt("cc786278981b0728"));
294 
295     /* check ed25519/x25519 key */
296     assert_true(test_cli_g10_key_sign("941822a0fc1b30a5"));
297     assert_true(test_cli_g10_key_encrypt("941822a0fc1b30a5"));
298     assert_false(test_cli_g10_key_sign("c711187e594376af"));
299     assert_true(test_cli_g10_key_encrypt("c711187e594376af"));
300 
301     /* check p256 key */
302     assert_true(test_cli_g10_key_sign("23674f21b2441527"));
303     assert_true(test_cli_g10_key_encrypt("23674f21b2441527"));
304     assert_false(test_cli_g10_key_sign("37e285e9e9851491"));
305     assert_true(test_cli_g10_key_encrypt("37e285e9e9851491"));
306 
307     /* check p384 key */
308     assert_true(test_cli_g10_key_sign("242a3aa5ea85f44a"));
309     assert_true(test_cli_g10_key_encrypt("242a3aa5ea85f44a"));
310     assert_false(test_cli_g10_key_sign("e210e3d554a4fad9"));
311     assert_true(test_cli_g10_key_encrypt("e210e3d554a4fad9"));
312 
313     /* check p521 key */
314     assert_true(test_cli_g10_key_sign("2092ca8324263b6a"));
315     assert_true(test_cli_g10_key_encrypt("2092ca8324263b6a"));
316     assert_false(test_cli_g10_key_sign("9853df2f6d297442"));
317     assert_true(test_cli_g10_key_encrypt("9853df2f6d297442"));
318 
319     /* check bp256 key */
320     assert_true(test_cli_g10_key_sign("d0c8a3daf9e0634a"));
321     assert_true(test_cli_g10_key_encrypt("d0c8a3daf9e0634a"));
322     assert_false(test_cli_g10_key_sign("2edabb94d3055f76"));
323     assert_true(test_cli_g10_key_encrypt("2edabb94d3055f76"));
324 
325     /* check bp384 key */
326     assert_true(test_cli_g10_key_sign("6cf2dce85599ada2"));
327     assert_true(test_cli_g10_key_encrypt("6cf2dce85599ada2"));
328     assert_false(test_cli_g10_key_sign("cff1bb6f16d28191"));
329     assert_true(test_cli_g10_key_encrypt("cff1bb6f16d28191"));
330 
331     /* check bp512 key */
332     assert_true(test_cli_g10_key_sign("aa5c58d14f7b8f48"));
333     assert_true(test_cli_g10_key_encrypt("aa5c58d14f7b8f48"));
334     assert_false(test_cli_g10_key_sign("20cdaa1482ba79ce"));
335     assert_true(test_cli_g10_key_encrypt("20cdaa1482ba79ce"));
336 
337     /* check secp256k1 key */
338     assert_true(test_cli_g10_key_sign("3ea5bb6f9692c1a0"));
339     assert_true(test_cli_g10_key_encrypt("3ea5bb6f9692c1a0"));
340     assert_false(test_cli_g10_key_sign("7635401f90d3e533"));
341     assert_true(test_cli_g10_key_encrypt("7635401f90d3e533"));
342 }
343 
TEST_F(rnp_tests,test_cli_rnpkeys_unicode)344 TEST_F(rnp_tests, test_cli_rnpkeys_unicode)
345 {
346 #ifdef _WIN32
347     std::string  uid_acp = "\x80@a.com";
348     std::wstring uid2_wide =
349       L"\x03C9\x0410@b.com"; // some Greek and Cyrillic for CreateProcessW test
350     char *rnpkeys_path = rnp_compose_path(original_dir(), "../rnpkeys/rnpkeys.exe", NULL);
351     std::string homedir_s = std::string(m_dir) + "/unicode";
352     rnp_mkdir(homedir_s.c_str());
353     std::string path_s = rnpkeys_path;
354     std::string cmdline_s = path_s + " --numbits 2048 --homedir " + homedir_s +
355                             " --password password --userid " + uid_acp + " --generate-key";
356     UINT         acp = GetACP();
357     STARTUPINFOA si;
358     ZeroMemory(&si, sizeof si);
359     PROCESS_INFORMATION pi;
360     ZeroMemory(&pi, sizeof pi);
361     BOOL res = CreateProcessA(NULL, // (LPSTR) path_s.c_str(), // Module name
362                               (LPSTR) cmdline_s.c_str(), // Command line
363                               NULL,                      // Process handle not inheritable
364                               NULL,                      // Thread handle not inheritable
365                               FALSE,                     // Handle inheritance
366                               0,                         // Creation flags
367                               NULL,                      // Use parent's environment block
368                               NULL,                      // Use parent's starting directory
369                               &si,                       // Pointer to STARTUPINFO structure
370                               &pi); // Pointer to PROCESS_INFORMATION structure
371     assert_true(res);
372     assert_true(WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_OBJECT_0);
373     CloseHandle(pi.hProcess);
374     CloseHandle(pi.hThread);
375 
376     std::wstring homedir_ws = wstr_from_utf8(homedir_s);
377     std::wstring path_ws = wstr_from_utf8(path_s);
378     std::wstring cmdline_ws = path_ws + L" --numbits 2048 --homedir " + homedir_ws +
379                               L" --password password --userid " + uid2_wide +
380                               L" --generate-key";
381     STARTUPINFOW siw;
382     ZeroMemory(&siw, sizeof siw);
383     ZeroMemory(&pi, sizeof pi);
384     res = CreateProcessW(NULL,
385                          (LPWSTR) cmdline_ws.c_str(), // Command line
386                          NULL,                        // Process handle not inheritable
387                          NULL,                        // Thread handle not inheritable
388                          FALSE,                       // Handle inheritance
389                          0,                           // Creation flags
390                          NULL,                        // Use parent's environment block
391                          NULL,                        // Use parent's starting directory
392                          &siw,                        // Pointer to STARTUPINFO structure
393                          &pi); // Pointer to PROCESS_INFORMATION structure
394     assert_true(res);
395     assert_true(WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_OBJECT_0);
396     CloseHandle(pi.hProcess);
397     CloseHandle(pi.hThread);
398     // Load the keyring and check what was actually written
399     rnp_ffi_t ffi;
400     assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
401     rnp_input_t input = NULL;
402     assert_rnp_success(rnp_input_from_path(&input, "unicode/pubring.gpg"));
403     assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
404     rnp_input_destroy(input);
405 
406     // convert from ACP to wide char via Windows native mechanism
407     int convertResult = MultiByteToWideChar(acp, 0, uid_acp.c_str(), uid_acp.size(), NULL, 0);
408     assert_true(convertResult > 0);
409     std::wstring uid_wide;
410     uid_wide.resize(convertResult);
411     convertResult = MultiByteToWideChar(
412       acp, 0, uid_acp.c_str(), uid_acp.size(), &uid_wide[0], (int) uid_wide.size());
413     assert_true(convertResult > 0);
414 
415     // we expect to find UID in UTF-8
416     std::string      uid_utf8 = wstr_to_utf8(uid_wide);
417     rnp_key_handle_t key = NULL;
418     assert_rnp_success(rnp_locate_key(ffi, "userid", uid_utf8.c_str(), &key));
419     assert_non_null(key);
420 
421     size_t uids = 0;
422     assert_rnp_success(rnp_key_get_uid_count(key, &uids));
423     assert_int_equal(uids, 1);
424 
425     rnp_uid_handle_t uid = NULL;
426     assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
427     assert_non_null(uid);
428 
429     size_t size = 0;
430     char * data = NULL;
431     assert_rnp_success(rnp_uid_get_data(uid, (void **) &data, &size));
432     std::string uid_read(data, data + size);
433     assert_int_equal(0, uid_read.compare(uid_utf8));
434     rnp_buffer_destroy(data);
435     rnp_uid_handle_destroy(uid);
436     rnp_key_handle_destroy(key);
437 
438     uid_utf8 = wstr_to_utf8(uid2_wide);
439     key = NULL;
440     assert_rnp_success(rnp_locate_key(ffi, "userid", uid_utf8.c_str(), &key));
441     assert_non_null(key);
442 
443     uids = 0;
444     assert_rnp_success(rnp_key_get_uid_count(key, &uids));
445     assert_int_equal(uids, 1);
446 
447     uid = NULL;
448     assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
449     assert_non_null(uid);
450 
451     size = 0;
452     data = NULL;
453     assert_rnp_success(rnp_uid_get_data(uid, (void **) &data, &size));
454     std::string uid2_read(data, data + size);
455     assert_int_equal(0, uid2_read.compare(uid_utf8));
456     rnp_buffer_destroy(data);
457     rnp_uid_handle_destroy(uid);
458     rnp_key_handle_destroy(key);
459     rnp_ffi_destroy(ffi);
460 #endif
461 }
462 
TEST_F(rnp_tests,test_cli_rnp)463 TEST_F(rnp_tests, test_cli_rnp)
464 {
465     int ret;
466     assert_int_equal(0, call_rnp("rnp", "--version", NULL));
467 
468     /* sign with default key */
469     ret = call_rnp("rnp",
470                    "--homedir",
471                    KEYS "/1",
472                    "--password",
473                    "password",
474                    "--sign",
475                    FILES "/hello.txt",
476                    NULL);
477     assert_int_equal(ret, 0);
478 
479     /* encrypt with default key */
480     ret = call_rnp(
481       "rnp", "--homedir", KEYS "/1", "--encrypt", FILES "/hello.txt", "--overwrite", NULL);
482     assert_int_equal(ret, 0);
483 
484     /* sign and verify back with g10 key */
485     ret = call_rnp("rnp",
486                    "--homedir",
487                    KEYS "/3",
488                    "-u",
489                    "4BE147BB22DF1E60",
490                    "--password",
491                    "password",
492                    "--sign",
493                    FILES "/hello.txt",
494                    "--overwrite",
495                    NULL);
496     assert_int_equal(ret, 0);
497     ret = call_rnp("rnp", "--homedir", KEYS "/3", "--verify", FILES "/hello.txt.pgp", NULL);
498     assert_int_equal(ret, 0);
499 
500     /* encrypt and decrypt back with g10 key */
501     ret = call_rnp("rnp",
502                    "--homedir",
503                    KEYS "/3",
504                    "-r",
505                    "4BE147BB22DF1E60",
506                    "--encrypt",
507                    FILES "/hello.txt",
508                    "--overwrite",
509                    NULL);
510     assert_int_equal(ret, 0);
511     ret = call_rnp("rnp",
512                    "--homedir",
513                    KEYS "/3",
514                    "--password",
515                    "password",
516                    "--decrypt",
517                    FILES "/hello.txt.pgp",
518                    NULL);
519     assert_int_equal(ret, 0);
520 }
521 
TEST_F(rnp_tests,test_cli_examples)522 TEST_F(rnp_tests, test_cli_examples)
523 {
524     char *examples_path = rnp_compose_path(original_dir(), "../examples", NULL);
525     char *example_path = NULL;
526     /* key generation example */
527     example_path = rnp_compose_path(examples_path, "generate", NULL);
528     assert_non_null(example_path);
529     assert_int_equal(system(example_path), 0);
530     free(example_path);
531 
532     /* encryption sample */
533     example_path = rnp_compose_path(examples_path, "encrypt", NULL);
534     assert_non_null(example_path);
535     assert_int_equal(system(example_path), 0);
536     free(example_path);
537 
538     /* decryption sample */
539     example_path = rnp_compose_path(examples_path, "decrypt", NULL);
540     assert_non_null(example_path);
541     assert_int_equal(system(example_path), 0);
542     free(example_path);
543 
544     /* signing sample */
545     example_path = rnp_compose_path(examples_path, "sign", NULL);
546     assert_non_null(example_path);
547     assert_int_equal(system(example_path), 0);
548     free(example_path);
549 
550     /* verification sample */
551     example_path = rnp_compose_path(examples_path, "verify", NULL);
552     assert_non_null(example_path);
553     assert_int_equal(system(example_path), 0);
554     free(example_path);
555 
556     free(examples_path);
557 }
558 
TEST_F(rnp_tests,test_cli_rnpkeys)559 TEST_F(rnp_tests, test_cli_rnpkeys)
560 {
561     int ret;
562     assert_int_equal(0, call_rnp("rnpkeys", "--version", NULL));
563 
564     /* test keys listing */
565     ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", NULL);
566     assert_int_equal(ret, 0);
567 
568     ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "--with-sigs", NULL);
569     assert_int_equal(ret, 0);
570 
571     ret = call_rnp("rnpkeys", "--homedir", KEYS "/2", "--list-keys", NULL);
572     assert_int_equal(ret, 0);
573 
574     ret = call_rnp("rnpkeys", "--homedir", KEYS "/2", "--list-keys", "--with-sigs", NULL);
575     assert_int_equal(ret, 0);
576 
577     ret = call_rnp("rnpkeys", "--homedir", KEYS "/3", "--list-keys", NULL);
578     assert_int_equal(ret, 0);
579 
580     ret = call_rnp("rnpkeys", "--homedir", KEYS "/3", "--list-keys", "--with-sigs", NULL);
581     assert_int_equal(ret, 0);
582 
583     ret = call_rnp("rnpkeys", "--homedir", KEYS "/5", "--list-keys", NULL);
584     assert_int_equal(ret, 0);
585 
586     ret = call_rnp("rnpkeys", "--homedir", KEYS "/5", "--list-keys", "--with-sigs", NULL);
587     assert_int_equal(ret, 0);
588 
589     ret = call_rnp("rnpkeys", "--homedir", G10KEYS, "--list-keys", NULL);
590     assert_int_equal(ret, 0);
591 
592     ret = call_rnp("rnpkeys", "--homedir", G10KEYS, "--list-keys", "--with-sigs", NULL);
593     assert_int_equal(ret, 0);
594 
595     /* test single key listing command */
596     ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "2fcadf05ffa501bb", NULL);
597     assert_int_equal(ret, 0);
598 
599     ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "00000000", NULL);
600     assert_int_not_equal(ret, 0);
601 
602     ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "zzzzzzzz", NULL);
603     assert_int_not_equal(ret, 0);
604 }
605 
606 // check both primary key and subkey for the given userid
607 static int
key_expiration_check(rnp_key_store_t * keystore,const char * userid,uint32_t expectedExpiration)608 key_expiration_check(rnp_key_store_t *keystore,
609                      const char *     userid,
610                      uint32_t         expectedExpiration)
611 {
612     int res = -1; // not found
613     for (auto &key : keystore->keys) {
614         pgp_key_t *pk;
615         if (key.is_primary()) {
616             pk = &key;
617         } else {
618             assert_true(key.has_primary_fp());
619             pk = rnp_key_store_get_key_by_fpr(keystore, key.primary_fp());
620         }
621         assert_int_equal(pk->uid_count(), 1);
622         auto uid = pk->get_uid(0).str;
623         if (uid != userid) {
624             continue;
625         }
626         auto expiration = key.expiration();
627         if (uid == "expiration_absolute@rnp" || uid == "expiration_beyond2038_absolute@rnp") {
628             auto diff = expectedExpiration < expiration ? expiration - expectedExpiration :
629                                                           expectedExpiration - expiration;
630             // allow 10 minutes diff
631             if (diff < 600) {
632                 res = 1;
633             } else {
634                 return 0;
635             }
636         } else {
637             if (expectedExpiration == expiration) {
638                 res = 1;
639             } else {
640                 RNP_LOG(
641                   "key_expiration_check error: userid=%s expectedExpiration=%u expiration=%u",
642                   userid,
643                   expectedExpiration,
644                   expiration);
645                 return 0;
646             }
647         }
648     }
649     return res;
650 }
651 
652 static int
key_generate(const char * homedir,const char * userid,const char * expiration)653 key_generate(const char *homedir, const char *userid, const char *expiration)
654 {
655     int ret = call_rnp("rnpkeys",
656                        "--password",
657                        "1234",
658                        "--homedir",
659                        homedir,
660                        "--generate-key",
661                        "--expiration",
662                        expiration,
663                        "--userid",
664                        userid,
665                        "--numbits",
666                        "1024",
667                        NULL);
668     return ret;
669 }
670 
TEST_F(rnp_tests,test_cli_rnpkeys_genkey)671 TEST_F(rnp_tests, test_cli_rnpkeys_genkey)
672 {
673     assert_false(RNP_MKDIR(GENKEYS, S_IRWXU));
674     time_t   basetime = time(NULL);
675     time_t   rawtime = basetime + 604800;
676     time_t   y2k38time = INT32_MAX;
677     uint32_t expected_diff_beyond2038_absolute;
678     if (rnp_y2k38_warning(y2k38time)) {
679         // we're on the system that doesn't support dates beyond y2k38
680         auto diff_to_y2k38 = y2k38time - basetime;
681         expected_diff_beyond2038_absolute = diff_to_y2k38;
682     } else {
683         time_t    now = time(NULL);
684         struct tm tm2100 = *localtime(&now);
685         tm2100.tm_hour = 0;
686         tm2100.tm_min = 0;
687         tm2100.tm_sec = 0;
688         tm2100.tm_mday = 1;
689         tm2100.tm_mon = 0;
690         tm2100.tm_year = 200;
691         expected_diff_beyond2038_absolute = mktime(&tm2100) - basetime;
692     }
693     struct tm *timeinfo = localtime(&rawtime);
694     // clear hours, minutes and seconds
695     timeinfo->tm_hour = 0;
696     timeinfo->tm_min = 0;
697     timeinfo->tm_sec = 0;
698     rawtime = mktime(timeinfo);
699     auto exp =
700       fmt("%d-%02d-%02d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday);
701 
702     // these should fail and not go to the keystore
703     assert_int_not_equal(key_generate(GENKEYS, "expiration_negative@rnp", "-1"), 0);
704     assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_1@rnp", "1z"), 0);
705     assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_2@rnp", "now"), 0);
706     assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_3@rnp", "00000-01-01"),
707                          0);
708     assert_int_not_equal(
709       key_generate(GENKEYS, "expiration_integer_overflow@rnp", "1234567890123456789"), 0);
710     assert_int_not_equal(key_generate(GENKEYS, "expiration_32bit_overflow@rnp", "4294967296"),
711                          0);
712     assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow_day@rnp", "2037-02-29"),
713                          0);
714     assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow_month@rnp", "2037-13-01"),
715                          0);
716     if (!rnp_y2k38_warning(y2k38time)) {
717         assert_int_not_equal(
718           key_generate(GENKEYS, "expiration_overflow_year@rnp", "2337-01-01"), 0);
719     }
720     assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_day@rnp", "2037-02-00"),
721                          0);
722     assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_month@rnp", "2037-00-01"),
723                          0);
724     assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_year@rnp", "1800-01-01"),
725                          0);
726     assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow@rnp", "200y"), 0);
727     assert_int_not_equal(key_generate(GENKEYS, "expiration_past@rnp", "2021-01-01"), 0);
728 
729     // these should pass and go to the keystore -- 15 primary keys and 15 subkeys
730     assert_int_equal(key_generate(GENKEYS, "expiration_beyond2038_relative@rnp", "20y"), 0);
731     assert_int_equal(key_generate(GENKEYS, "expiration_beyond2038_absolute@rnp", "2100-01-01"),
732                      0);
733     assert_int_equal(key_generate(GENKEYS, "expiration_absolute@rnp", exp.c_str()), 0);
734     assert_int_equal(key_generate(GENKEYS, "expiration_max_32bit@rnp", "4294967295"), 0);
735     assert_int_equal(key_generate(GENKEYS, "expiration_max_32bit_h@rnp", "1193046h"), 0);
736     assert_int_equal(key_generate(GENKEYS, "expiration_1sec@rnp", "1"), 0);
737     assert_int_equal(key_generate(GENKEYS, "expiration_1hour@rnp", "1h"), 0);
738     assert_int_equal(key_generate(GENKEYS, "expiration_1day@rnp", "1d"), 0);
739     assert_int_equal(key_generate(GENKEYS, "expiration_1week@rnp", "1w"), 0);
740     assert_int_equal(key_generate(GENKEYS, "expiration_1month@rnp", "1m"), 0);
741     assert_int_equal(key_generate(GENKEYS, "expiration_1year@rnp", "1y"), 0);
742     assert_int_equal(key_generate(GENKEYS, "expiration_2sec@rnp", "2"), 0);
743     assert_int_equal(key_generate(GENKEYS, "expiration_2hours@rnp", "2h"), 0);
744     assert_int_equal(key_generate(GENKEYS, "expiration_2days@rnp", "2d"), 0);
745     assert_int_equal(key_generate(GENKEYS, "expiration_2weeks@rnp", "2w"), 0);
746     assert_int_equal(key_generate(GENKEYS, "expiration_2months@rnp", "2m"), 0);
747     assert_int_equal(key_generate(GENKEYS, "expiration_2years@rnp", "2y"), 0);
748 
749     auto         keystore = new rnp_key_store_t(PGP_KEY_STORE_GPG, "");
750     pgp_source_t src = {};
751     assert_rnp_success(init_file_src(&src, GENKEYS "/pubring.gpg"));
752     assert_true(rnp_key_store_load_from_src(keystore, &src, NULL));
753     assert_int_equal(rnp_key_store_get_key_count(keystore), 34);
754     src_close(&src);
755     assert_int_equal(key_expiration_check(keystore, "expiration_max_32bit@rnp", 4294967295),
756                      1);
757     assert_int_equal(key_expiration_check(keystore, "expiration_max_32bit_h@rnp", 4294965600),
758                      1);
759     assert_int_equal(key_expiration_check(keystore, "expiration_1sec@rnp", 1), 1);
760     assert_int_equal(key_expiration_check(keystore, "expiration_1hour@rnp", 3600), 1);
761     assert_int_equal(key_expiration_check(keystore, "expiration_1day@rnp", 86400), 1);
762     assert_int_equal(key_expiration_check(keystore, "expiration_1week@rnp", 604800), 1);
763     assert_int_equal(key_expiration_check(keystore, "expiration_1month@rnp", 2678400), 1);
764     assert_int_equal(key_expiration_check(keystore, "expiration_1year@rnp", 31536000), 1);
765     assert_int_equal(key_expiration_check(keystore, "expiration_2sec@rnp", 2), 1);
766     assert_int_equal(key_expiration_check(keystore, "expiration_2hours@rnp", 7200), 1);
767     assert_int_equal(key_expiration_check(keystore, "expiration_2days@rnp", 172800), 1);
768     assert_int_equal(key_expiration_check(keystore, "expiration_2weeks@rnp", 1209600), 1);
769     assert_int_equal(key_expiration_check(keystore, "expiration_2months@rnp", 5356800), 1);
770     assert_int_equal(key_expiration_check(keystore, "expiration_2years@rnp", 63072000), 1);
771     assert_int_equal(
772       key_expiration_check(keystore, "expiration_absolute@rnp", rawtime - basetime), 1);
773     assert_int_equal(key_expiration_check(keystore,
774                                           "expiration_beyond2038_absolute@rnp",
775                                           expected_diff_beyond2038_absolute),
776                      1);
777     assert_int_equal(
778       key_expiration_check(keystore, "expiration_beyond2038_relative@rnp", 630720000), 1);
779 
780     delete keystore;
781     delete_recursively(GENKEYS);
782 }
783 
TEST_F(rnp_tests,test_cli_dump)784 TEST_F(rnp_tests, test_cli_dump)
785 {
786     char *dump_path = rnp_compose_path(original_dir(), "../examples/dump", NULL);
787     char  cmd[512] = {0};
788     int   chnum;
789     int   status;
790     /* call dump's help */
791     chnum = snprintf(cmd, sizeof(cmd), "%s -h", dump_path);
792     assert_true(chnum < (int) sizeof(cmd));
793     status = system(cmd);
794     assert_true(WIFEXITED(status));
795     assert_int_equal(WEXITSTATUS(status), 1);
796     /* run dump on some data */
797     chnum = snprintf(cmd, sizeof(cmd), "%s \"%s\"", dump_path, KEYS "/1/pubring.gpg");
798     assert_true(chnum < (int) sizeof(cmd));
799     status = system(cmd);
800     assert_true(WIFEXITED(status));
801     assert_int_equal(WEXITSTATUS(status), 0);
802     /* run dump on some data with json output */
803     chnum = snprintf(cmd, sizeof(cmd), "%s -j \"%s\"", dump_path, KEYS "/1/pubring.gpg");
804     assert_true(chnum < (int) sizeof(cmd));
805     status = system(cmd);
806     assert_true(WIFEXITED(status));
807     assert_int_equal(WEXITSTATUS(status), 0);
808     /* run dump on directory - must fail but not crash */
809     chnum = snprintf(cmd, sizeof(cmd), "%s \"%s\"", dump_path, KEYS "/1/");
810     assert_true(chnum < (int) sizeof(cmd));
811     status = system(cmd);
812     assert_true(WIFEXITED(status));
813     assert_int_not_equal(WEXITSTATUS(status), 0);
814 
815     free(dump_path);
816 }
817 
TEST_F(rnp_tests,test_cli_logname)818 TEST_F(rnp_tests, test_cli_logname)
819 {
820     char *      logname = getenv("LOGNAME");
821     char *      user = getenv("USER");
822     std::string testname(user ? user : "user");
823     testname.append("-test-user");
824 
825     setenv("LOGNAME", testname.c_str(), 1);
826     assert_string_equal(getenv_logname(), testname.c_str());
827     if (user) {
828         unsetenv("LOGNAME");
829         assert_string_equal(getenv_logname(), user);
830     }
831 
832     if (logname) {
833         setenv("LOGNAME", logname, 1);
834     } else {
835         unsetenv("LOGNAME");
836     }
837 }
838