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