1 /*
2  * Copyright (c) 2017-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 "support.h"
28 #include "rnp_tests.h"
29 #include "file-utils.h"
30 
31 #ifdef _MSC_VER
32 #include "uniwin.h"
33 #include <shlwapi.h>
34 #else
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #endif
38 
39 #include <stdlib.h>
40 #include <stdbool.h>
41 #include <stdio.h>
42 
43 #include <crypto.h>
44 #include <pgp-key.h>
45 #include <fstream>
46 #include <vector>
47 #include <algorithm>
48 
49 #ifndef WINSHELLAPI
50 #include <ftw.h>
51 #endif
52 
53 #ifdef _WIN32
54 int
setenv(const char * name,const char * value,int overwrite)55 setenv(const char *name, const char *value, int overwrite)
56 {
57     if (getenv(name) && !overwrite) {
58         return 0;
59     }
60     char varbuf[512] = {0};
61     snprintf(varbuf, sizeof(varbuf) - 1, "%s=%s", name, value);
62     return _putenv(varbuf);
63 }
64 
65 int
unsetenv(const char * name)66 unsetenv(const char *name)
67 {
68     char varbuf[512] = {0};
69     snprintf(varbuf, sizeof(varbuf) - 1, "%s=", name);
70     return _putenv(varbuf);
71 }
72 #endif
73 
74 /* Check if a file is empty
75  * Use with assert_true and rnp_assert_false(rstate, .
76  */
77 bool
file_empty(const char * path)78 file_empty(const char *path)
79 {
80     return file_size(path) == 0;
81 }
82 
83 std::string
file_to_str(const std::string & path)84 file_to_str(const std::string &path)
85 {
86     // TODO: wstring path _WIN32
87     std::ifstream infile(path);
88     assert_true(infile);
89     return std::string(std::istreambuf_iterator<char>(infile),
90                        std::istreambuf_iterator<char>());
91 }
92 
93 std::vector<uint8_t>
file_to_vec(const std::string & path)94 file_to_vec(const std::string &path)
95 {
96     // TODO: wstring path _WIN32
97     std::ifstream stream(path, std::ios::in | std::ios::binary);
98     assert_true(stream);
99     return std::vector<uint8_t>((std::istreambuf_iterator<char>(stream)),
100                                 std::istreambuf_iterator<char>());
101 }
102 
103 off_t
file_size(const char * path)104 file_size(const char *path)
105 {
106     struct stat path_stat;
107     if (rnp_stat(path, &path_stat) != -1) {
108         if (S_ISDIR(path_stat.st_mode)) {
109             return -1;
110         }
111         return path_stat.st_size;
112     }
113     return -1;
114 }
115 
116 /* Concatenate multiple strings into a full path.
117  * A directory separator is added between components.
118  * Must be called in between va_start and va_end.
119  * Final argument of calling function must be NULL.
120  */
121 void
vpaths_concat(char * buffer,size_t buffer_size,const char * first,va_list ap)122 vpaths_concat(char *buffer, size_t buffer_size, const char *first, va_list ap)
123 {
124     size_t      length = strlen(first);
125     const char *s;
126 
127     assert_true(length < buffer_size);
128 
129     memset(buffer, 0, buffer_size);
130 
131     strncpy(buffer, first, buffer_size - 1);
132     while ((s = va_arg(ap, const char *))) {
133         length += strlen(s) + 1;
134         assert_true(length < buffer_size);
135         strncat(buffer, "/", buffer_size - 1);
136         strncat(buffer, s, buffer_size - 1);
137     }
138 }
139 
140 /* Concatenate multiple strings into a full path.
141  * Final argument must be NULL.
142  */
143 char *
paths_concat(char * buffer,size_t buffer_length,const char * first,...)144 paths_concat(char *buffer, size_t buffer_length, const char *first, ...)
145 {
146     va_list ap;
147 
148     va_start(ap, first);
149     vpaths_concat(buffer, buffer_length, first, ap);
150     va_end(ap);
151     return buffer;
152 }
153 
154 /* Concatenate multiple strings into a full path and
155  * check that the file exists.
156  * Final argument must be NULL.
157  */
158 int
path_rnp_file_exists(const char * first,...)159 path_rnp_file_exists(const char *first, ...)
160 {
161     va_list ap;
162     char    buffer[512] = {0};
163 
164     va_start(ap, first);
165     vpaths_concat(buffer, sizeof(buffer), first, ap);
166     va_end(ap);
167     return rnp_file_exists(buffer);
168 }
169 
170 /* Concatenate multiple strings into a full path and
171  * create the directory.
172  * Final argument must be NULL.
173  */
174 void
path_mkdir(mode_t mode,const char * first,...)175 path_mkdir(mode_t mode, const char *first, ...)
176 {
177     va_list ap;
178     char    buffer[512];
179 
180     va_start(ap, first);
181     vpaths_concat(buffer, sizeof(buffer), first, ap);
182     va_end(ap);
183 
184     assert_int_equal(0, RNP_MKDIR(buffer, mode));
185 }
186 
187 static int
remove_cb(const char * fpath,const struct stat * sb,int typeflag,struct FTW * ftwbuf)188 remove_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
189 {
190     int ret = remove(fpath);
191     if (ret)
192         perror(fpath);
193 
194     return ret;
195 }
196 
197 static const char *
get_tmp()198 get_tmp()
199 {
200     const char *tmp = getenv("TEMP");
201     return tmp ? tmp : "/tmp";
202 }
203 
204 static bool
is_tmp_path(const char * path)205 is_tmp_path(const char *path)
206 {
207     char *rlpath = realpath(path, NULL);
208     if (!rlpath) {
209         rlpath = strdup(path);
210     }
211     const char *tmp = get_tmp();
212     char *      rltmp = realpath(tmp, NULL);
213     if (!rltmp) {
214         rltmp = strdup(tmp);
215     }
216     bool res = rlpath && rltmp && !strncmp(rlpath, rltmp, strlen(rltmp));
217     free(rlpath);
218     free(rltmp);
219     return res;
220 }
221 
222 /* Recursively remove a directory.
223  * The path must be located in /tmp, for safety.
224  */
225 void
delete_recursively(const char * path)226 delete_recursively(const char *path)
227 {
228     bool relative =
229 #ifdef _MSC_VER
230       PathIsRelativeA(path);
231 #else
232       *path != '/';
233 #endif
234     char *fullpath = const_cast<char *>(path);
235     if (relative) {
236         char *cwd = getcwd(NULL, 0);
237         fullpath = rnp_compose_path(cwd, path, NULL);
238         free(cwd);
239     }
240     /* sanity check, we should only be purging things from /tmp/ */
241     assert_true(is_tmp_path(fullpath));
242 
243 #ifdef WINSHELLAPI
244     SHFILEOPSTRUCTA fileOp = {};
245     fileOp.fFlags = FOF_NOCONFIRMATION;
246     assert_true(strlen(fullpath) < MAX_PATH);
247     char newFrom[MAX_PATH + 1];
248     strcpy_s(newFrom, fullpath);
249     newFrom[strlen(fullpath) + 1] = NULL; // two NULLs are required
250     fileOp.pFrom = newFrom;
251     fileOp.pTo = NULL;
252     fileOp.wFunc = FO_DELETE;
253     fileOp.hNameMappings = NULL;
254     fileOp.hwnd = NULL;
255     fileOp.lpszProgressTitle = NULL;
256     assert_int_equal(0, SHFileOperationA(&fileOp));
257 #else
258     nftw(path, remove_cb, 64, FTW_DEPTH | FTW_PHYS);
259     if (*path != '/') {
260         free(fullpath);
261     }
262 #endif
263 }
264 
265 void
copy_recursively(const char * src,const char * dst)266 copy_recursively(const char *src, const char *dst)
267 {
268     /* sanity check, we should only be copying things to /tmp/ */
269     assert_true(is_tmp_path(dst));
270 
271 #ifdef WINSHELLAPI
272     SHFILEOPSTRUCTA fileOp = {};
273     fileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR;
274     fileOp.pFrom = src;
275     fileOp.pTo = dst;
276     assert_true(strlen(src) < MAX_PATH);
277     char newFrom[MAX_PATH + 1];
278     strcpy_s(newFrom, src);
279     newFrom[strlen(src) + 1] = NULL; // two NULLs are required
280     fileOp.pFrom = newFrom;
281     assert_true(strlen(dst) < MAX_PATH);
282     char newTo[MAX_PATH + 1];
283     strcpy_s(newTo, dst);
284     newTo[strlen(dst) + 1] = NULL; // two NULLs are required
285     fileOp.wFunc = FO_COPY;
286     fileOp.hNameMappings = NULL;
287     fileOp.hwnd = NULL;
288     fileOp.lpszProgressTitle = NULL;
289     assert_int_equal(0, SHFileOperationA(&fileOp));
290 #else
291     // TODO: maybe use fts or something less hacky
292     char buf[2048];
293 #ifndef _WIN32
294     snprintf(buf, sizeof(buf), "cp -a '%s' '%s'", src, dst);
295 #else
296     snprintf(buf, sizeof(buf), "xcopy \"%s\" \"%s\" /I /Q /E /Y", src, dst);
297 #endif // _WIN32
298     assert_int_equal(0, system(buf));
299 #endif // WINSHELLAPI
300 }
301 
302 /* Creates and returns a temporary directory path.
303  * Caller must free the string.
304  */
305 #if defined(HAVE_MKDTEMP)
306 char *
make_temp_dir()307 make_temp_dir()
308 {
309     char rltmp[PATH_MAX] = {0};
310     if (!realpath(get_tmp(), rltmp)) {
311         printf("Fatal: realpath on tmp folder failed. Error %d.\n", errno);
312         return NULL;
313     }
314 
315     const char *tmplate = "/rnp-gtest-XXXXXX";
316     char *      buffer = (char *) calloc(1, strlen(rltmp) + strlen(tmplate) + 1);
317     if (buffer == NULL) {
318         return NULL;
319     }
320     memcpy(buffer, rltmp, strlen(rltmp));
321     memcpy(buffer + strlen(rltmp), tmplate, strlen(tmplate));
322     buffer[strlen(rltmp) + strlen(tmplate)] = '\0';
323     char *res = mkdtemp(buffer);
324     if (!res) {
325         free(buffer);
326     }
327     return res;
328 }
329 #elif defined(HAVE__TEMPNAM)
330 char *
make_temp_dir()331 make_temp_dir()
332 {
333     const int MAX_ATTEMPTS = 10;
334     for (int i = 0; i < MAX_ATTEMPTS; i++) {
335         char *dir = _tempnam(NULL, "rnp-gtest-");
336         if (!dir) {
337             fprintf(stderr, "_tempnam failed to generate temporary path");
338             continue;
339         }
340         if (RNP_MKDIR(dir, S_IRWXU)) {
341             fprintf(stderr, "Failed to create temporary directory");
342             free(dir);
343             continue;
344         }
345         return dir;
346     }
347     fprintf(stderr, "Failed to make temporary directory, aborting");
348     return NULL;
349 }
350 #else
351 #error Unsupported platform
352 #endif
353 
354 static char *
directory_from_absolute_file_path(const char * file_path)355 directory_from_absolute_file_path(const char *file_path)
356 {
357     const char *last_sep = strrchr(file_path, '/');
358     if (!last_sep) {
359         return NULL;
360     }
361 
362     size_t file_path_len = (last_sep - file_path);
363     size_t dir_len = file_path_len + 1;
364     char * dir = (char *) calloc(1, dir_len);
365     if (!dir) {
366         return NULL;
367     }
368     strncpy(dir, file_path, file_path_len);
369 
370     char *full_dir = realpath(dir, NULL);
371     free(dir);
372     dir = NULL;
373     return full_dir;
374 }
375 
376 static char *
directory_from_relative_file_path(const char * file_path,const char * reldir)377 directory_from_relative_file_path(const char *file_path, const char *reldir)
378 {
379     const char *last_sep = strrchr(file_path, '/');
380     if (!last_sep) {
381         return NULL;
382     }
383 
384     size_t file_path_len = (last_sep - file_path);
385     size_t dir_len = strlen(reldir) + 1 + file_path_len + 1;
386     char * dir = (char *) calloc(1, dir_len);
387     if (!dir) {
388         return NULL;
389     }
390 
391     strncpy(dir, reldir, dir_len);
392     strncat(dir, "/", dir_len);
393     strncat(dir, file_path, file_path_len);
394 
395     char *full_dir = realpath(dir, NULL);
396     free(dir);
397     dir = NULL;
398     return full_dir;
399 }
400 
401 char *
directory_from_file_path(const char * file_path,const char * reldir)402 directory_from_file_path(const char *file_path, const char *reldir)
403 {
404     if (!file_path) {
405         return NULL;
406     }
407     if (*file_path == '/') {
408         return directory_from_absolute_file_path(file_path);
409     }
410     return directory_from_relative_file_path(file_path, reldir);
411 }
412 
413 bool
bin_eq_hex(const uint8_t * data,size_t len,const char * val)414 bin_eq_hex(const uint8_t *data, size_t len, const char *val)
415 {
416     uint8_t *dec;
417     size_t   stlen = strlen(val);
418     if (stlen != len * 2) {
419         return false;
420     }
421 
422     assert_non_null(dec = (uint8_t *) malloc(len));
423     assert_true(rnp::hex_decode(val, dec, len));
424     bool res = !memcmp(data, dec, len);
425     free(dec);
426     return res;
427 }
428 
429 bool
hex2mpi(pgp_mpi_t * val,const char * hex)430 hex2mpi(pgp_mpi_t *val, const char *hex)
431 {
432     const size_t hex_len = strlen(hex);
433     size_t       buf_len = hex_len / 2;
434     bool         ok;
435 
436     uint8_t *buf = NULL;
437 
438     buf = (uint8_t *) malloc(buf_len);
439 
440     if (buf == NULL) {
441         return false;
442     }
443 
444     rnp::hex_decode(hex, buf, buf_len);
445 
446     ok = mem2mpi(val, buf, buf_len);
447     free(buf);
448     return ok;
449 }
450 
451 bool
cmp_keyid(const pgp_key_id_t & id,const std::string & val)452 cmp_keyid(const pgp_key_id_t &id, const std::string &val)
453 {
454     return bin_eq_hex(id.data(), id.size(), val.c_str());
455 }
456 
457 bool
cmp_keyfp(const pgp_fingerprint_t & fp,const std::string & val)458 cmp_keyfp(const pgp_fingerprint_t &fp, const std::string &val)
459 {
460     return bin_eq_hex(fp.fingerprint, fp.length, val.c_str());
461 }
462 
463 int
test_value_equal(const char * what,const char * expected_value,const uint8_t v[],size_t v_len)464 test_value_equal(const char *what, const char *expected_value, const uint8_t v[], size_t v_len)
465 {
466     assert_int_equal(strlen(expected_value), v_len * 2);
467     char *produced = (char *) calloc(1, v_len * 2 + 1);
468     if (!produced) {
469         return -1;
470     }
471     rnp::hex_encode(v, v_len, produced, v_len * 2 + 1);
472     assert_string_equal(produced, expected_value);
473     free(produced);
474     return 0;
475 }
476 
477 bool
mpi_empty(const pgp_mpi_t & val)478 mpi_empty(const pgp_mpi_t &val)
479 {
480     pgp_mpi_t zero{};
481     return (val.len == 0) && !memcmp(val.mpi, zero.mpi, PGP_MPINT_SIZE);
482 }
483 
484 char *
uint_to_string(char * buff,const int buffsize,unsigned int num,int base)485 uint_to_string(char *buff, const int buffsize, unsigned int num, int base)
486 {
487     char *ptr;
488     ptr = &buff[buffsize - 1];
489     *ptr = '\0';
490 
491     do {
492         *--ptr = "0123456789abcdef"[num % base];
493         num /= base;
494     } while (num != 0);
495 
496     return ptr;
497 }
498 
499 bool
write_pass_to_pipe(int fd,size_t count)500 write_pass_to_pipe(int fd, size_t count)
501 {
502     const char *const password = "passwordforkeygeneration\n";
503     for (size_t i = 0; i < count; i++) {
504         const char *p = password;
505         ssize_t     remaining = strlen(p);
506 
507         do {
508             ssize_t written = write(fd, p, remaining);
509             if (written <= 0) {
510                 perror("write");
511                 return false;
512             }
513             p += written;
514             remaining -= written;
515         } while (remaining);
516     }
517     return true;
518 }
519 
520 bool
setupPasswordfd(int * pipefd)521 setupPasswordfd(int *pipefd)
522 {
523     bool ok = false;
524 
525     if (pipe(pipefd) == -1) {
526         perror("pipe");
527         goto end;
528     }
529     // write it twice for normal keygen (primary+sub)
530     if (!write_pass_to_pipe(pipefd[1], 2)) {
531         close(pipefd[1]);
532         goto end;
533     }
534     ok = true;
535 
536 end:
537     close(pipefd[1]);
538     return ok;
539 }
540 
541 static bool
setup_rnp_cfg(rnp_cfg & cfg,const char * ks_format,const char * homedir,int * pipefd)542 setup_rnp_cfg(rnp_cfg &cfg, const char *ks_format, const char *homedir, int *pipefd)
543 {
544     bool res;
545     char pubpath[MAXPATHLEN];
546     char secpath[MAXPATHLEN];
547     char homepath[MAXPATHLEN];
548 
549     /* set password fd if any */
550     if (pipefd) {
551         if (!(res = setupPasswordfd(pipefd))) {
552             return res;
553         }
554         cfg.set_int(CFG_PASSFD, pipefd[0]);
555         // pipefd[0] will be closed via passfp
556         pipefd[0] = -1;
557     }
558     /* setup keyring paths */
559     if (homedir == NULL) {
560         /* if we use default homedir then we append '.rnp' and create directory as well */
561         homedir = getenv("HOME");
562         paths_concat(homepath, sizeof(homepath), homedir, ".rnp", NULL);
563         if (!rnp_dir_exists(homepath)) {
564             path_mkdir(0700, homepath, NULL);
565         }
566         homedir = homepath;
567     }
568 
569     if (homedir == NULL) {
570         return false;
571     }
572 
573     cfg.set_str(CFG_KR_PUB_FORMAT, ks_format);
574     cfg.set_str(CFG_KR_SEC_FORMAT, ks_format);
575 
576     if (strcmp(ks_format, RNP_KEYSTORE_GPG) == 0) {
577         paths_concat(pubpath, MAXPATHLEN, homedir, PUBRING_GPG, NULL);
578         paths_concat(secpath, MAXPATHLEN, homedir, SECRING_GPG, NULL);
579     } else if (strcmp(ks_format, RNP_KEYSTORE_KBX) == 0) {
580         paths_concat(pubpath, MAXPATHLEN, homedir, PUBRING_KBX, NULL);
581         paths_concat(secpath, MAXPATHLEN, homedir, SECRING_KBX, NULL);
582     } else if (strcmp(ks_format, RNP_KEYSTORE_G10) == 0) {
583         paths_concat(pubpath, MAXPATHLEN, homedir, PUBRING_G10, NULL);
584         paths_concat(secpath, MAXPATHLEN, homedir, SECRING_G10, NULL);
585     } else if (strcmp(ks_format, RNP_KEYSTORE_GPG21) == 0) {
586         paths_concat(pubpath, MAXPATHLEN, homedir, PUBRING_KBX, NULL);
587         paths_concat(secpath, MAXPATHLEN, homedir, SECRING_G10, NULL);
588         cfg.set_str(CFG_KR_PUB_FORMAT, RNP_KEYSTORE_KBX);
589         cfg.set_str(CFG_KR_SEC_FORMAT, RNP_KEYSTORE_G10);
590     } else {
591         return false;
592     }
593 
594     cfg.set_str(CFG_KR_PUB_PATH, (char *) pubpath);
595     cfg.set_str(CFG_KR_SEC_PATH, (char *) secpath);
596     return true;
597 }
598 
599 bool
setup_cli_rnp_common(cli_rnp_t * rnp,const char * ks_format,const char * homedir,int * pipefd)600 setup_cli_rnp_common(cli_rnp_t *rnp, const char *ks_format, const char *homedir, int *pipefd)
601 {
602     rnp_cfg cfg;
603     if (!setup_rnp_cfg(cfg, ks_format, homedir, pipefd)) {
604         return false;
605     }
606 
607     /*initialize the basic RNP structure. */
608     return rnp->init(cfg);
609 }
610 
611 void
cli_set_default_rsa_key_desc(rnp_cfg & cfg,const char * hashalg)612 cli_set_default_rsa_key_desc(rnp_cfg &cfg, const char *hashalg)
613 {
614     cfg.set_int(CFG_NUMBITS, 1024);
615     cfg.set_str(CFG_HASH, hashalg);
616     cfg.set_int(CFG_S2K_ITER, 1);
617     cli_rnp_set_generate_params(cfg);
618 }
619 
620 // this is a password callback that will always fail
621 bool
failing_password_callback(const pgp_password_ctx_t * ctx,char * password,size_t password_size,void * userdata)622 failing_password_callback(const pgp_password_ctx_t *ctx,
623                           char *                    password,
624                           size_t                    password_size,
625                           void *                    userdata)
626 {
627     return false;
628 }
629 
630 bool
ffi_failing_password_provider(rnp_ffi_t ffi,void * app_ctx,rnp_key_handle_t key,const char * pgp_context,char * buf,size_t buf_len)631 ffi_failing_password_provider(rnp_ffi_t        ffi,
632                               void *           app_ctx,
633                               rnp_key_handle_t key,
634                               const char *     pgp_context,
635                               char *           buf,
636                               size_t           buf_len)
637 {
638     return false;
639 }
640 
641 bool
ffi_asserting_password_provider(rnp_ffi_t ffi,void * app_ctx,rnp_key_handle_t key,const char * pgp_context,char * buf,size_t buf_len)642 ffi_asserting_password_provider(rnp_ffi_t        ffi,
643                                 void *           app_ctx,
644                                 rnp_key_handle_t key,
645                                 const char *     pgp_context,
646                                 char *           buf,
647                                 size_t           buf_len)
648 {
649     assert_false(true);
650     return false;
651 }
652 
653 bool
ffi_string_password_provider(rnp_ffi_t ffi,void * app_ctx,rnp_key_handle_t key,const char * pgp_context,char * buf,size_t buf_len)654 ffi_string_password_provider(rnp_ffi_t        ffi,
655                              void *           app_ctx,
656                              rnp_key_handle_t key,
657                              const char *     pgp_context,
658                              char *           buf,
659                              size_t           buf_len)
660 {
661     size_t pass_len = strlen((const char *) app_ctx);
662     if (pass_len >= buf_len) {
663         return false;
664     }
665     memcpy(buf, app_ctx, pass_len + 1);
666     return true;
667 }
668 
669 // this is a password callback that should never be called
670 bool
asserting_password_callback(const pgp_password_ctx_t * ctx,char * password,size_t password_size,void * userdata)671 asserting_password_callback(const pgp_password_ctx_t *ctx,
672                             char *                    password,
673                             size_t                    password_size,
674                             void *                    userdata)
675 {
676     assert_false(true);
677     return false;
678 }
679 
680 // this is a password callback that just copies the string in userdata to
681 // the password buffer
682 bool
string_copy_password_callback(const pgp_password_ctx_t * ctx,char * password,size_t password_size,void * userdata)683 string_copy_password_callback(const pgp_password_ctx_t *ctx,
684                               char *                    password,
685                               size_t                    password_size,
686                               void *                    userdata)
687 {
688     const char *str = (const char *) userdata;
689     strncpy(password, str, password_size - 1);
690     return true;
691 }
692 
693 bool
starts_with(const std::string & data,const std::string & match)694 starts_with(const std::string &data, const std::string &match)
695 {
696     return data.find(match) == 0;
697 }
698 
699 bool
ends_with(const std::string & data,const std::string & match)700 ends_with(const std::string &data, const std::string &match)
701 {
702     return data.size() >= match.size() &&
703            data.substr(data.size() - match.size(), match.size()) == match;
704 }
705 
706 std::string
fmt(const char * format,...)707 fmt(const char *format, ...)
708 {
709     int     size;
710     va_list ap;
711 
712     va_start(ap, format);
713     size = vsnprintf(NULL, 0, format, ap);
714     va_end(ap);
715 
716     // +1 for terminating null
717     std::string buf(size + 1, '\0');
718 
719     va_start(ap, format);
720     size = vsnprintf(&buf[0], buf.size(), format, ap);
721     va_end(ap);
722 
723     // drop terminating null
724     buf.resize(size);
725     return buf;
726 }
727 
728 std::string
strip_eol(const std::string & str)729 strip_eol(const std::string &str)
730 {
731     size_t endpos = str.find_last_not_of("\r\n");
732     if (endpos != std::string::npos) {
733         return str.substr(0, endpos + 1);
734     }
735     return str;
736 }
737 
738 std::string
lowercase(const std::string & str)739 lowercase(const std::string &str)
740 {
741     std::string res = str;
742     std::transform(
743       res.begin(), res.end(), res.begin(), [](unsigned char ch) { return std::tolower(ch); });
744     return res;
745 }
746 
747 static bool
jso_get_field(json_object * obj,json_object ** fld,const std::string & name)748 jso_get_field(json_object *obj, json_object **fld, const std::string &name)
749 {
750     if (!obj || !json_object_is_type(obj, json_type_object)) {
751         return false;
752     }
753     return json_object_object_get_ex(obj, name.c_str(), fld);
754 }
755 
756 bool
check_json_field_str(json_object * obj,const std::string & field,const std::string & value)757 check_json_field_str(json_object *obj, const std::string &field, const std::string &value)
758 {
759     json_object *fld = NULL;
760     if (!jso_get_field(obj, &fld, field)) {
761         return false;
762     }
763     if (!json_object_is_type(fld, json_type_string)) {
764         return false;
765     }
766     const char *jsoval = json_object_get_string(fld);
767     return jsoval && (value == jsoval);
768 }
769 
770 bool
check_json_field_int(json_object * obj,const std::string & field,int value)771 check_json_field_int(json_object *obj, const std::string &field, int value)
772 {
773     json_object *fld = NULL;
774     if (!jso_get_field(obj, &fld, field)) {
775         return false;
776     }
777     if (!json_object_is_type(fld, json_type_int)) {
778         return false;
779     }
780     return json_object_get_int(fld) == value;
781 }
782 
783 bool
check_json_field_bool(json_object * obj,const std::string & field,bool value)784 check_json_field_bool(json_object *obj, const std::string &field, bool value)
785 {
786     json_object *fld = NULL;
787     if (!jso_get_field(obj, &fld, field)) {
788         return false;
789     }
790     if (!json_object_is_type(fld, json_type_boolean)) {
791         return false;
792     }
793     return json_object_get_boolean(fld) == value;
794 }
795 
796 bool
check_json_pkt_type(json_object * pkt,int tag)797 check_json_pkt_type(json_object *pkt, int tag)
798 {
799     if (!pkt || !json_object_is_type(pkt, json_type_object)) {
800         return false;
801     }
802     json_object *hdr = NULL;
803     if (!json_object_object_get_ex(pkt, "header", &hdr)) {
804         return false;
805     }
806     if (!json_object_is_type(hdr, json_type_object)) {
807         return false;
808     }
809     return check_json_field_int(hdr, "tag", tag);
810 }
811 
812 static bool
ishex(const std::string & hexid)813 ishex(const std::string &hexid)
814 {
815     /* duplicates str_is_hex from fficli.cpp */
816     size_t hexlen = hexid.length();
817     size_t hexidx = 0;
818     if ((hexlen >= 2) && (hexid[0] == '0') && ((hexid[1] == 'x') || (hexid[1] == 'X'))) {
819         hexidx += 2;
820     }
821 
822     for (size_t i = hexidx; i < hexlen; i++) {
823         if ((hexid[i] >= '0') && (hexid[i] <= '9')) {
824             continue;
825         }
826         if ((hexid[i] >= 'a') && (hexid[i] <= 'f')) {
827             continue;
828         }
829         if ((hexid[i] >= 'A') && (hexid[i] <= 'F')) {
830             continue;
831         }
832         if ((hexid[i] == ' ') || (hexid[i] == '\t')) {
833             continue;
834         }
835         return false;
836     }
837     return true;
838 }
839 
840 pgp_key_t *
rnp_tests_get_key_by_id(rnp_key_store_t * keyring,const std::string & keyid,pgp_key_t * after)841 rnp_tests_get_key_by_id(rnp_key_store_t *keyring, const std::string &keyid, pgp_key_t *after)
842 {
843     if (!keyring || keyid.empty() || !ishex(keyid)) {
844         return NULL;
845     }
846     pgp_key_id_t keyid_bin = {};
847     size_t       binlen = rnp::hex_decode(keyid.c_str(), keyid_bin.data(), keyid_bin.size());
848     if (binlen > PGP_KEY_ID_SIZE) {
849         return NULL;
850     }
851     pgp_key_search_t search = {};
852     search.by.keyid = keyid_bin;
853     search.type = PGP_KEY_SEARCH_KEYID;
854     return rnp_key_store_search(keyring, &search, after);
855 }
856 
857 pgp_key_t *
rnp_tests_get_key_by_grip(rnp_key_store_t * keyring,const std::string & grip)858 rnp_tests_get_key_by_grip(rnp_key_store_t *keyring, const std::string &grip)
859 {
860     if (!keyring || grip.empty() || !ishex(grip)) {
861         return NULL;
862     }
863     pgp_key_grip_t grip_bin = {};
864     size_t         binlen = rnp::hex_decode(grip.c_str(), grip_bin.data(), grip_bin.size());
865     if (binlen > PGP_KEY_GRIP_SIZE) {
866         return NULL;
867     }
868     return rnp_tests_get_key_by_grip(keyring, grip_bin);
869 }
870 
871 pgp_key_t *
rnp_tests_get_key_by_grip(rnp_key_store_t * keyring,const pgp_key_grip_t & grip)872 rnp_tests_get_key_by_grip(rnp_key_store_t *keyring, const pgp_key_grip_t &grip)
873 {
874     if (!keyring) {
875         return NULL;
876     }
877     pgp_key_search_t search = {};
878     search.by.grip = grip;
879     search.type = PGP_KEY_SEARCH_GRIP;
880     return rnp_key_store_search(keyring, &search, NULL);
881 }
882 
883 pgp_key_t *
rnp_tests_get_key_by_fpr(rnp_key_store_t * keyring,const std::string & keyid)884 rnp_tests_get_key_by_fpr(rnp_key_store_t *keyring, const std::string &keyid)
885 {
886     if (!keyring || keyid.empty() || !ishex(keyid)) {
887         return NULL;
888     }
889     std::vector<uint8_t> keyid_bin(PGP_FINGERPRINT_SIZE, 0);
890     size_t binlen = rnp::hex_decode(keyid.c_str(), keyid_bin.data(), keyid_bin.size());
891     if (binlen > PGP_FINGERPRINT_SIZE) {
892         return NULL;
893     }
894     pgp_fingerprint_t fp = {{}, static_cast<unsigned>(binlen)};
895     memcpy(fp.fingerprint, keyid_bin.data(), binlen);
896     return rnp_key_store_get_key_by_fpr(keyring, fp);
897 }
898 
899 pgp_key_t *
rnp_tests_key_search(rnp_key_store_t * keyring,const std::string & uid)900 rnp_tests_key_search(rnp_key_store_t *keyring, const std::string &uid)
901 {
902     if (!keyring || uid.empty()) {
903         return NULL;
904     }
905 
906     pgp_key_search_t srch_userid = {PGP_KEY_SEARCH_USERID};
907     strncpy(srch_userid.by.userid, uid.c_str(), sizeof(srch_userid.by.userid));
908     srch_userid.by.userid[sizeof(srch_userid.by.userid) - 1] = '\0';
909     return rnp_key_store_search(keyring, &srch_userid, NULL);
910 }
911 
912 void
reload_pubring(rnp_ffi_t * ffi)913 reload_pubring(rnp_ffi_t *ffi)
914 {
915     rnp_output_t output = NULL;
916     assert_rnp_success(rnp_output_to_memory(&output, 0));
917     assert_rnp_success(rnp_save_keys(*ffi, "GPG", output, RNP_LOAD_SAVE_PUBLIC_KEYS));
918     assert_rnp_success(rnp_ffi_destroy(*ffi));
919 
920     /* get output */
921     uint8_t *buf = NULL;
922     size_t   len = 0;
923     assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false));
924     rnp_input_t input = NULL;
925     assert_rnp_success(rnp_input_from_memory(&input, buf, len, false));
926 
927     /* re-init ffi and load keys */
928     assert_rnp_success(rnp_ffi_create(ffi, "GPG", "GPG"));
929     assert_rnp_success(rnp_import_keys(*ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL));
930     assert_rnp_success(rnp_output_destroy(output));
931     assert_rnp_success(rnp_input_destroy(input));
932 }
933 
934 void
reload_keyrings(rnp_ffi_t * ffi)935 reload_keyrings(rnp_ffi_t *ffi)
936 {
937     rnp_output_t outpub = NULL;
938     assert_rnp_success(rnp_output_to_memory(&outpub, 0));
939     assert_rnp_success(rnp_save_keys(*ffi, "GPG", outpub, RNP_LOAD_SAVE_PUBLIC_KEYS));
940     rnp_output_t outsec = NULL;
941     assert_rnp_success(rnp_output_to_memory(&outsec, 0));
942     assert_rnp_success(rnp_save_keys(*ffi, "GPG", outsec, RNP_LOAD_SAVE_SECRET_KEYS));
943     assert_rnp_success(rnp_ffi_destroy(*ffi));
944     /* re-init ffi and load keys */
945     assert_rnp_success(rnp_ffi_create(ffi, "GPG", "GPG"));
946 
947     uint8_t *buf = NULL;
948     size_t   len = 0;
949     assert_rnp_success(rnp_output_memory_get_buf(outpub, &buf, &len, false));
950     rnp_input_t input = NULL;
951     assert_rnp_success(rnp_input_from_memory(&input, buf, len, false));
952     assert_rnp_success(rnp_import_keys(*ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL));
953     assert_rnp_success(rnp_input_destroy(input));
954     assert_rnp_success(rnp_output_destroy(outpub));
955 
956     assert_rnp_success(rnp_output_memory_get_buf(outsec, &buf, &len, false));
957     assert_rnp_success(rnp_input_from_memory(&input, buf, len, false));
958     assert_rnp_success(rnp_import_keys(*ffi, input, RNP_LOAD_SAVE_SECRET_KEYS, NULL));
959     assert_rnp_success(rnp_input_destroy(input));
960     assert_rnp_success(rnp_output_destroy(outsec));
961 }
962 
963 static bool
load_keys_internal(rnp_ffi_t ffi,const std::string & format,const std::string & path,bool secret)964 load_keys_internal(rnp_ffi_t          ffi,
965                    const std::string &format,
966                    const std::string &path,
967                    bool               secret)
968 {
969     if (path.empty()) {
970         return true;
971     }
972     rnp_input_t input = NULL;
973     if (rnp_input_from_path(&input, path.c_str())) {
974         return false;
975     }
976     bool res = !rnp_load_keys(ffi,
977                               format.c_str(),
978                               input,
979                               secret ? RNP_LOAD_SAVE_SECRET_KEYS : RNP_LOAD_SAVE_PUBLIC_KEYS);
980     rnp_input_destroy(input);
981     return res;
982 }
983 
984 bool
load_keys_gpg(rnp_ffi_t ffi,const std::string & pub,const std::string & sec)985 load_keys_gpg(rnp_ffi_t ffi, const std::string &pub, const std::string &sec)
986 {
987     return load_keys_internal(ffi, "GPG", pub, false) &&
988            load_keys_internal(ffi, "GPG", sec, true);
989 }
990 
991 bool
load_keys_kbx_g10(rnp_ffi_t ffi,const std::string & pub,const std::string & sec)992 load_keys_kbx_g10(rnp_ffi_t ffi, const std::string &pub, const std::string &sec)
993 {
994     return load_keys_internal(ffi, "KBX", pub, false) &&
995            load_keys_internal(ffi, "G10", sec, true);
996 }
997 
998 static bool
import_keys(rnp_ffi_t ffi,const std::string & path,uint32_t flags)999 import_keys(rnp_ffi_t ffi, const std::string &path, uint32_t flags)
1000 {
1001     rnp_input_t input = NULL;
1002     if (rnp_input_from_path(&input, path.c_str())) {
1003         return false;
1004     }
1005     bool res = !rnp_import_keys(ffi, input, flags, NULL);
1006     rnp_input_destroy(input);
1007     return res;
1008 }
1009 
1010 bool
import_all_keys(rnp_ffi_t ffi,const std::string & path)1011 import_all_keys(rnp_ffi_t ffi, const std::string &path)
1012 {
1013     return import_keys(ffi, path, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
1014 }
1015 
1016 bool
import_pub_keys(rnp_ffi_t ffi,const std::string & path)1017 import_pub_keys(rnp_ffi_t ffi, const std::string &path)
1018 {
1019     return import_keys(ffi, path, RNP_LOAD_SAVE_PUBLIC_KEYS);
1020 }
1021 
1022 bool
import_sec_keys(rnp_ffi_t ffi,const std::string & path)1023 import_sec_keys(rnp_ffi_t ffi, const std::string &path)
1024 {
1025     return import_keys(ffi, path, RNP_LOAD_SAVE_SECRET_KEYS);
1026 }
1027 
1028 static bool
import_keys(rnp_ffi_t ffi,const uint8_t * data,size_t len,uint32_t flags)1029 import_keys(rnp_ffi_t ffi, const uint8_t *data, size_t len, uint32_t flags)
1030 {
1031     rnp_input_t input = NULL;
1032     if (rnp_input_from_memory(&input, data, len, false)) {
1033         return false;
1034     }
1035     bool res = !rnp_import_keys(ffi, input, flags, NULL);
1036     rnp_input_destroy(input);
1037     return res;
1038 }
1039 
1040 bool
import_all_keys(rnp_ffi_t ffi,const uint8_t * data,size_t len)1041 import_all_keys(rnp_ffi_t ffi, const uint8_t *data, size_t len)
1042 {
1043     return import_keys(ffi, data, len, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS);
1044 }
1045 
1046 bool
import_pub_keys(rnp_ffi_t ffi,const uint8_t * data,size_t len)1047 import_pub_keys(rnp_ffi_t ffi, const uint8_t *data, size_t len)
1048 {
1049     return import_keys(ffi, data, len, RNP_LOAD_SAVE_PUBLIC_KEYS);
1050 }
1051 
1052 bool
import_sec_keys(rnp_ffi_t ffi,const uint8_t * data,size_t len)1053 import_sec_keys(rnp_ffi_t ffi, const uint8_t *data, size_t len)
1054 {
1055     return import_keys(ffi, data, len, RNP_LOAD_SAVE_SECRET_KEYS);
1056 }
1057 
1058 std::vector<uint8_t>
export_key(rnp_key_handle_t key,bool armored,bool secret)1059 export_key(rnp_key_handle_t key, bool armored, bool secret)
1060 {
1061     uint32_t flags = RNP_KEY_EXPORT_SUBKEYS;
1062     if (armored) {
1063         flags = flags | RNP_KEY_EXPORT_ARMORED;
1064     }
1065     if (secret) {
1066         flags = flags | RNP_KEY_EXPORT_SECRET;
1067     } else {
1068         flags = flags | RNP_KEY_EXPORT_PUBLIC;
1069     }
1070     rnp_output_t output = NULL;
1071     rnp_output_to_memory(&output, 0);
1072     rnp_key_export(key, output, flags);
1073     size_t   len = 0;
1074     uint8_t *buf = NULL;
1075     rnp_output_memory_get_buf(output, &buf, &len, false);
1076     std::vector<uint8_t> res(buf, buf + len);
1077     rnp_output_destroy(output);
1078     return res;
1079 }
1080 
1081 void
dump_key_stdout(rnp_key_handle_t key,bool secret)1082 dump_key_stdout(rnp_key_handle_t key, bool secret)
1083 {
1084     auto pub = export_key(key, true, false);
1085     printf("%.*s", (int) pub.size(), (char *) pub.data());
1086     if (!secret) {
1087         return;
1088     }
1089     auto sec = export_key(key, true, true);
1090     printf("%.*s", (int) sec.size(), (char *) sec.data());
1091 }
1092 
1093 bool
check_uid_valid(rnp_key_handle_t key,size_t idx,bool valid)1094 check_uid_valid(rnp_key_handle_t key, size_t idx, bool valid)
1095 {
1096     rnp_uid_handle_t uid = NULL;
1097     if (rnp_key_get_uid_handle_at(key, idx, &uid)) {
1098         return false;
1099     }
1100     bool val = !valid;
1101     rnp_uid_is_valid(uid, &val);
1102     rnp_uid_handle_destroy(uid);
1103     return val == valid;
1104 }
1105 
1106 bool
check_uid_primary(rnp_key_handle_t key,size_t idx,bool primary)1107 check_uid_primary(rnp_key_handle_t key, size_t idx, bool primary)
1108 {
1109     rnp_uid_handle_t uid = NULL;
1110     if (rnp_key_get_uid_handle_at(key, idx, &uid)) {
1111         return false;
1112     }
1113     bool prim = !primary;
1114     rnp_uid_is_primary(uid, &prim);
1115     rnp_uid_handle_destroy(uid);
1116     return prim == primary;
1117 }
1118 
1119 bool
check_key_valid(rnp_key_handle_t key,bool validity)1120 check_key_valid(rnp_key_handle_t key, bool validity)
1121 {
1122     bool valid = !validity;
1123     if (rnp_key_is_valid(key, &valid)) {
1124         return false;
1125     }
1126     return valid == validity;
1127 }
1128 
1129 uint32_t
get_key_expiry(rnp_key_handle_t key)1130 get_key_expiry(rnp_key_handle_t key)
1131 {
1132     uint32_t expiry = (uint32_t) -1;
1133     rnp_key_get_expiration(key, &expiry);
1134     return expiry;
1135 }
1136 
1137 size_t
get_key_uids(rnp_key_handle_t key)1138 get_key_uids(rnp_key_handle_t key)
1139 {
1140     size_t count = (size_t) -1;
1141     rnp_key_get_uid_count(key, &count);
1142     return count;
1143 }
1144 
1145 bool
check_sub_valid(rnp_key_handle_t key,size_t idx,bool validity)1146 check_sub_valid(rnp_key_handle_t key, size_t idx, bool validity)
1147 {
1148     rnp_key_handle_t sub = NULL;
1149     if (rnp_key_get_subkey_at(key, idx, &sub)) {
1150         return false;
1151     }
1152     bool valid = !validity;
1153     rnp_key_is_valid(sub, &valid);
1154     rnp_key_handle_destroy(sub);
1155     return valid == validity;
1156 }
1157 
1158 bool
sm2_enabled()1159 sm2_enabled()
1160 {
1161     bool enabled = false;
1162     if (rnp_supports_feature(RNP_FEATURE_PK_ALG, "SM2", &enabled)) {
1163         return false;
1164     }
1165     return enabled;
1166 }
1167 
1168 bool
aead_eax_enabled()1169 aead_eax_enabled()
1170 {
1171     bool enabled = false;
1172     if (rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "EAX", &enabled)) {
1173         return false;
1174     }
1175     return enabled;
1176 }
1177 
1178 bool
aead_ocb_enabled()1179 aead_ocb_enabled()
1180 {
1181     bool enabled = false;
1182     if (rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "OCB", &enabled)) {
1183         return false;
1184     }
1185     return enabled;
1186 }
1187 
1188 bool
twofish_enabled()1189 twofish_enabled()
1190 {
1191     bool enabled = false;
1192     if (rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "Twofish", &enabled)) {
1193         return false;
1194     }
1195     return enabled;
1196 }
1197 
1198 bool
brainpool_enabled()1199 brainpool_enabled()
1200 {
1201     bool enabled = false;
1202     if (rnp_supports_feature(RNP_FEATURE_CURVE, "brainpoolP256r1", &enabled)) {
1203         return false;
1204     }
1205     return enabled;
1206 }
1207