diff options
202 files changed, 10168 insertions, 5057 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ec5aae1c7..1313cbe04 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -2,3 +2,5 @@ 6a80d6f9206eae2c867c45daa5cd3e7d6c6ad114 # doc: Fix spelling errors found by lintian. 2ed1f68b48db7b5503045386de0500fddf70077e +# indent: Re-indent a function +869d1df270c0ccc3a9f792167b96d678a932b37e @@ -220,6 +220,9 @@ Jussi Kivilinna <[email protected]> Kyle Butt <[email protected]> 2013-05-29:CAAODAYLbCtqOG6msLLL0UTdASKWT6u2ptxsgUQ1JpusBESBoNQ@mail.gmail.com: +Mario Haustein <[email protected]> +2022-09-26:8149069.T7Z3S40VBb@localdomain: + Michael Haubenwallner <[email protected]> 2018-07-13:[email protected]: diff --git a/Makefile.am b/Makefile.am index 1b6933484..b6b8a8e9e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ WITH_MSI=1 # Location of the released tarball archives. This is prefixed by # the variable RELEASE_ARCHIVE in ~/.gnupg-autogen.rc. For example: # RELEASE_ARCHIVE=user@host:archive/tarballs -RELEASE_ARCHIVE_SUFFIX = gnupg/v2.4 +RELEASE_ARCHIVE_SUFFIX = gnupg/v2.5 # The variable RELEASE_SIGNKEY in ~/.gnupg-autogen.rc is used # to specify the key for signing. For example: # RELEASE_SIGNKEY=D8692123C4065DEA5E0F3AB5249B39D24F25E3B6 @@ -1,6 +1,8 @@ -Noteworthy changes in version 2.4.6 (unreleased) +Noteworthy changes in version 2.5.0 (unreleased) ------------------------------------------------ + Changes also found in 2.4.6: + * gpg: New command --quick-set-ownertrust. [rG967678d972] * gpg: Indicate disabled keys in key listings and add list option @@ -18,11 +20,8 @@ Noteworthy changes in version 2.4.6 (unreleased) * gpgconf: Check readability of some files with -X and change its output format. [rG759adb2493] - Release-info: https://dev.gnupg.org/T7030 - -Noteworthy changes in version 2.4.5 (2024-03-07) ------------------------------------------------- + Changes also found in 2.4.5: * gpg,gpgv: New option --assert-pubkey-algo. [T6946] @@ -63,11 +62,8 @@ Noteworthy changes in version 2.4.5 (2024-03-07) * Make the getswdb.sh tool usable outside the GnuPG tree. - Release-info: https://dev.gnupg.org/T6960 - -Noteworthy changes in version 2.4.4 (2024-01-25) ------------------------------------------------- + Changes also found in 2.4.4: * gpg: Do not keep an unprotected smartcard backup key on disk. See https://gnupg.org/blog/20240125-smartcard-backup-key.html for a @@ -168,11 +164,7 @@ Noteworthy changes in version 2.4.4 (2024-01-25) * Improve the speedo build system for Unix. [T6710] - Release-info: https://dev.gnupg.org/T6578 - - -Noteworthy changes in version 2.4.3 (2023-07-04) ------------------------------------------------- + Changes also found in 2.4.3: * gpg: Set default expiration date to 3 years. [T2701] @@ -221,12 +213,8 @@ Noteworthy changes in version 2.4.3 (2023-07-04) * Fix garbled time output in non-English Windows. [T6741] - See-also: gnupg-announce/2023q3/000480.html - Release-info: https://dev.gnupg.org/T6509 - -Noteworthy changes in version 2.4.2 (2023-05-30) ------------------------------------------------- + Changes also found in 2.4.2: * gpg: Print a warning if no more encryption subkeys are left over after changing the expiration date. [rGef2c3d50fa] @@ -248,8 +236,16 @@ Noteworthy changes in version 2.4.2 (2023-05-30) * w32: Avoid using the VirtualStore. [T6403] - See-also: gnupg-announce/2023q2/000479.html - Release-info: https://dev.gnupg.org/T6506 + +Release dates of 2.4 versions +----------------------------- + +Version 2.4.5 (2024-03-07) https://dev.gnupg.org/T6960 +Version 2.4.4 (2024-01-25) https://dev.gnupg.org/T6578 +Version 2.4.3 (2023-07-04) https://dev.gnupg.org/T6509 +Version 2.4.2 (2023-05-30) https://dev.gnupg.org/T6506 +Version 2.4.1 (2023-04-28) https://dev.gnupg.org/T6454 +Version 2.4.0 (2022-12-16) https://dev.gnupg.org/T6302 Noteworthy changes in version 2.4.1 (2023-04-28) @@ -1,6 +1,6 @@ The GNU Privacy Guard ======================= - Version 2.4 + Version 2.5 (devel) Copyright 1997-2019 Werner Koch Copyright 1998-2021 Free Software Foundation, Inc. diff --git a/agent/agent.h b/agent/agent.h index 06bc1e046..dbb3000dd 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -288,8 +288,11 @@ struct server_control_s unsigned int raw_value: 1; unsigned int is_pss: 1; /* DATA holds PSS formated data. */ } digest; + unsigned int have_keygrip: 1; + unsigned int have_keygrip1: 1; unsigned char keygrip[20]; - int have_keygrip; + unsigned char keygrip1[20]; /* Another keygrip for hybrid crypto. */ + /* A flag to enable a hack to send the PKAUTH command instead of the PKSIGN command to the scdaemon. */ @@ -428,6 +431,7 @@ void *get_agent_daemon_notify_event (void); #endif void agent_sighup_action (void); int map_pk_openpgp_to_gcry (int openpgp_algo); +void agent_kick_the_loop (void); /*-- command.c --*/ gpg_error_t agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid, @@ -533,7 +537,7 @@ int agent_clear_passphrase (ctrl_t ctrl, /*-- cache.c --*/ void initialize_module_cache (void); void deinitialize_module_cache (void); -void agent_cache_housekeeping (void); +struct timespec *agent_cache_expiration (void); void agent_flush_cache (int pincache_only); int agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, const char *data, int ttl); @@ -556,6 +560,18 @@ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, const unsigned char *ciphertext, size_t ciphertextlen, membuf_t *outbuf, int *r_padding); +enum kemids + { + KEM_PQC_PGP, + KEM_PGP, + KEM_CMS + }; + +gpg_error_t agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, + const unsigned char *ct, size_t ctlen, + const unsigned char *option, size_t optionlen, + membuf_t *outbuf); + /*-- genkey.c --*/ #define CHECK_CONSTRAINTS_NOT_EMPTY 1 #define CHECK_CONSTRAINTS_NEW_SYMKEY 2 @@ -689,6 +705,9 @@ gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno, const char *keyref, const char *keydata, size_t keydatalen); +gpg_error_t agent_card_ecc_kem (ctrl_t ctrl, const unsigned char *ecc_ct, + size_t ecc_point_len, unsigned char *ecc_ecdh); + /*-- call-daemon.c --*/ gpg_error_t daemon_start (enum daemon_type type, ctrl_t ctrl); assuan_context_t daemon_type_ctx (enum daemon_type type, ctrl_t ctrl); @@ -737,6 +756,7 @@ int agent_card_pkdecrypt (ctrl_t ctrl, const char *desc_text, const unsigned char *indata, size_t indatalen, char **r_buf, size_t *r_buflen, int *r_padding); + int agent_card_readcert (ctrl_t ctrl, const char *id, char **r_buf, size_t *r_buflen); int agent_card_readkey (ctrl_t ctrl, const char *id, @@ -758,7 +778,6 @@ void agent_card_free_keyinfo (struct card_key_info_s *l); gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, int cap, struct card_key_info_s **result); - /*-- learncard.c --*/ int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force); diff --git a/agent/cache.c b/agent/cache.c index 7616dafc1..e8544205f 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -53,8 +53,20 @@ struct secret_data_s { char data[1]; /* A string. */ }; -/* The cache object. */ +/* The type of cache object. */ typedef struct cache_item_s *ITEM; + +/* The timer entry in a linked list. */ +struct timer_s { + ITEM next; + int tv_sec; + int reason; +}; +#define CACHE_EXPIRE_UNUSED 0 +#define CACHE_EXPIRE_LAST_ACCESS 1 +#define CACHE_EXPIRE_CREATION 2 + +/* The cache object. */ struct cache_item_s { ITEM next; time_t created; @@ -63,12 +75,18 @@ struct cache_item_s { struct secret_data_s *pw; cache_mode_t cache_mode; int restricted; /* The value of ctrl->restricted is part of the key. */ + struct timer_s t; char key[1]; }; /* The cache himself. */ static ITEM thecache; +/* The timer list of expiration, in active. */ +static ITEM the_timer_list; +/* Newly created entries, to be inserted into the timer list. */ +static ITEM the_timer_list_new; + /* NULL or the last cache key stored by agent_store_cache_hit. */ static char *last_stored_cache_key; @@ -193,100 +211,302 @@ new_data (const char *string, struct secret_data_s **r_data) } +static void +insert_to_timer_list_new (ITEM entry) +{ + entry->t.next = the_timer_list_new; + the_timer_list_new = entry; +} -/* Check whether there are items to expire. */ +/* Insert to the active timer list. */ static void -housekeeping (void) +insert_to_timer_list (struct timespec *ts, ITEM entry) { - ITEM r, rprev; - time_t current = gnupg_get_time (); + ITEM e, eprev; - /* First expire the actual data */ - for (r=thecache; r; r = r->next) + if (!the_timer_list || ts->tv_sec >= entry->t.tv_sec) { - if (r->cache_mode == CACHE_MODE_PIN) - ; /* Don't let it expire - scdaemon explicitly flushes them. */ - else if (r->pw && r->ttl >= 0 && r->accessed + r->ttl < current) + if (the_timer_list) { - if (DBG_CACHE) - log_debug (" expired '%s'.%d (%ds after last access)\n", - r->key, r->restricted, r->ttl); - release_data (r->pw); - r->pw = NULL; - r->accessed = current; + the_timer_list->t.tv_sec += ts->tv_sec - entry->t.tv_sec; + if (ts->tv_nsec >= 500000000) + the_timer_list->t.tv_sec++; } + + ts->tv_sec = entry->t.tv_sec; + ts->tv_nsec = 0; + + entry->t.tv_sec = 0; + entry->t.next = the_timer_list; + the_timer_list = entry; + return; } - /* Second, make sure that we also remove them based on the created - * stamp so that the user has to enter it from time to time. We - * don't do this for data items which are used to storage secrets in - * meory and are not user entered passphrases etc. */ - for (r=thecache; r; r = r->next) + entry->t.tv_sec -= ts->tv_sec; + eprev = NULL; + for (e = the_timer_list; e; e = e->t.next) { - unsigned long maxttl; + if (e->t.tv_sec > entry->t.tv_sec) + break; - switch (r->cache_mode) - { - case CACHE_MODE_DATA: - case CACHE_MODE_PIN: - continue; /* No MAX TTL here. */ - case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break; - default: maxttl = opt.max_cache_ttl; break; - } - if (r->pw && r->created + maxttl < current) - { - if (DBG_CACHE) - log_debug (" expired '%s'.%d (%lus after creation)\n", - r->key, r->restricted, opt.max_cache_ttl); - release_data (r->pw); - r->pw = NULL; - r->accessed = current; - } + eprev = e; + entry->t.tv_sec -= e->t.tv_sec; + } + + entry->t.next = e; + if (e) + e->t.tv_sec -= entry->t.tv_sec; + + if (eprev) + eprev->t.next = entry; + else + the_timer_list = entry; +} + +static void +remove_from_timer_list (ITEM entry) +{ + ITEM e, eprev; + + eprev = NULL; + for (e = the_timer_list; e; e = e->t.next) + if (e != entry) + eprev = e; + else + { + if (e->t.next) + e->t.next->t.tv_sec += e->t.tv_sec; + + if (eprev) + eprev->t.next = e->t.next; + else + the_timer_list = e->t.next; + + break; + } + + entry->t.next = NULL; + entry->t.tv_sec = 0; +} + +static void +remove_from_timer_list_new (ITEM entry) +{ + ITEM e, eprev; + + eprev = NULL; + for (e = the_timer_list_new; e; e = e->t.next) + if (e != entry) + eprev = e; + else + { + if (eprev) + eprev->t.next = e->t.next; + else + the_timer_list_new = e->t.next; + + break; + } + + entry->t.next = NULL; + entry->t.tv_sec = 0; +} + +static int +compute_expiration (ITEM r) +{ + unsigned long maxttl; + time_t current = gnupg_get_time (); + time_t next; + + if (r->cache_mode == CACHE_MODE_PIN) + return 0; /* Don't let it expire - scdaemon explicitly flushes them. */ + + if (!r->pw) + { + /* Expire an old and unused entry after 30 minutes. */ + r->t.tv_sec = 60*30; + r->t.reason = CACHE_EXPIRE_UNUSED; + return 1; } - /* Third, make sure that we don't have too many items in the list. - * Expire old and unused entries after 30 minutes. */ - for (rprev=NULL, r=thecache; r; ) + switch (r->cache_mode) { - if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current) - { - ITEM r2 = r->next; - if (DBG_CACHE) - log_debug (" removed '%s'.%d (mode %d) (slot not used for 30m)\n", - r->key, r->restricted, r->cache_mode); - xfree (r); - if (!rprev) - thecache = r2; - else - rprev->next = r2; - r = r2; - } - else + case CACHE_MODE_DATA: + case CACHE_MODE_PIN: + maxttl = 0; /* No MAX TTL here. */ + break; + case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break; + default: maxttl = opt.max_cache_ttl; break; + } + + if (maxttl) + { + if (r->created + maxttl < current) { - rprev = r; - r = r->next; + r->t.tv_sec = 0; + r->t.reason = CACHE_EXPIRE_CREATION; + return 1; } + + next = r->created + maxttl - current; + } + else + next = 0; + + if (r->ttl >= 0 && (next == 0 || r->ttl < next)) + { + r->t.tv_sec = r->ttl; + r->t.reason = CACHE_EXPIRE_LAST_ACCESS; + return 1; } + + if (next) + { + r->t.tv_sec = next; + r->t.reason = CACHE_EXPIRE_CREATION; + return 1; + } + + return 0; } +static void +update_expiration (ITEM entry, int is_new_entry) +{ + if (!is_new_entry) + { + remove_from_timer_list (entry); + remove_from_timer_list_new (entry); + } -void -agent_cache_housekeeping (void) + if (compute_expiration (entry)) + { + insert_to_timer_list_new (entry); + agent_kick_the_loop (); + } +} + + +/* Expire the cache entry. Returns 1 when the entry should be removed + * from the cache. */ +static int +do_expire (ITEM e) { - int res; + if (!e->pw) + /* Unused entry after 30 minutes. */ + return 1; - if (DBG_CACHE) - log_debug ("agent_cache_housekeeping\n"); + if (e->t.reason == CACHE_EXPIRE_LAST_ACCESS) + { + if (DBG_CACHE) + log_debug (" expired '%s'.%d (%ds after last access)\n", + e->key, e->restricted, e->ttl); + } + else + { + if (DBG_CACHE) + log_debug (" expired '%s'.%d (%lus after creation)\n", + e->key, e->restricted, opt.max_cache_ttl); + } + + release_data (e->pw); + e->pw = NULL; + e->accessed = 0; + + if (compute_expiration (e)) + insert_to_timer_list_new (e); + + return 0; +} + + +struct timespec * +agent_cache_expiration (void) +{ + static struct timespec abstime; + static struct timespec timeout; + struct timespec *tp; + struct timespec curtime; + int res; + int expired = 0; + ITEM e, enext; res = npth_mutex_lock (&cache_lock); if (res) log_fatal ("failed to acquire cache mutex: %s\n", strerror (res)); - housekeeping (); + npth_clock_gettime (&curtime); + if (the_timer_list) + { + if (npth_timercmp (&abstime, &curtime, <)) + expired = 1; + else + npth_timersub (&abstime, &curtime, &timeout); + } + + if (expired && (e = the_timer_list) && e->t.tv_sec == 0) + { + the_timer_list = e->t.next; + e->t.next = NULL; + + if (do_expire (e)) + { + ITEM r, rprev; + + if (DBG_CACHE) + log_debug (" removed '%s'.%d (mode %d) (slot not used for 30m)\n", + e->key, e->restricted, e->cache_mode); + + rprev = NULL; + for (r = thecache; r; r = r->next) + if (r == e) + { + if (!rprev) + thecache = r->next; + else + rprev->next = r->next; + break; + } + else + rprev = r; + + remove_from_timer_list_new (e); + + xfree (e); + } + } + + if (expired || !the_timer_list) + timeout.tv_sec = timeout.tv_nsec = 0; + + for (e = the_timer_list_new; e; e = enext) + { + enext = e->t.next; + e->t.next = NULL; + insert_to_timer_list (&timeout, e); + } + the_timer_list_new = NULL; + + if (!the_timer_list) + tp = NULL; + else + { + if (the_timer_list->t.tv_sec != 0) + { + timeout.tv_sec += the_timer_list->t.tv_sec; + the_timer_list->t.tv_sec = 0; + } + + npth_timeradd (&timeout, &curtime, &abstime); + tp = &timeout; + } res = npth_mutex_unlock (&cache_lock); if (res) log_fatal ("failed to release cache mutex: %s\n", strerror (res)); + + return tp; } @@ -314,6 +534,7 @@ agent_flush_cache (int pincache_only) release_data (r->pw); r->pw = NULL; r->accessed = 0; + update_expiration (r, 0); } } @@ -358,7 +579,6 @@ agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, if (DBG_CACHE) log_debug ("agent_put_cache '%s'.%d (mode %d) requested ttl=%d\n", key, restricted, cache_mode, ttl); - housekeeping (); if (!ttl) { @@ -410,6 +630,7 @@ agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, err = new_data (data, &r->pw); if (err) log_error ("error replacing cache item: %s\n", gpg_strerror (err)); + update_expiration (r, 0); } } else if (data) /* Insert. */ @@ -431,6 +652,7 @@ agent_put_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode, { r->next = thecache; thecache = r; + update_expiration (r, 1); } } if (err) @@ -478,7 +700,6 @@ agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode) log_debug ("agent_get_cache '%s'.%d (mode %d)%s ...\n", key, restricted, cache_mode, last_stored? " (stored cache key)":""); - housekeeping (); for (r=thecache; r; r = r->next) { @@ -500,7 +721,10 @@ agent_get_cache (ctrl_t ctrl, const char *key, cache_mode_t cache_mode) * below. Note also that we don't update the accessed time * for data items. */ if (r->cache_mode != CACHE_MODE_DATA) - r->accessed = gnupg_get_time (); + { + r->accessed = gnupg_get_time (); + update_expiration (r, 0); + } if (DBG_CACHE) log_debug ("... hit\n"); if (r->pw->totallen < 32) diff --git a/agent/call-daemon.c b/agent/call-daemon.c index e1c5669e9..806ef5dc1 100644 --- a/agent/call-daemon.c +++ b/agent/call-daemon.c @@ -98,7 +98,6 @@ static npth_mutex_t start_daemon_lock; struct wait_child_thread_parm_s { enum daemon_type type; - pid_t pid; }; @@ -109,54 +108,14 @@ wait_child_thread (void *arg) int err; struct wait_child_thread_parm_s *parm = arg; enum daemon_type type = parm->type; - pid_t pid = parm->pid; -#ifndef HAVE_W32_SYSTEM - int wstatus; -#endif const char *name = opt.daemon_program[type]; struct daemon_global_s *g = &daemon_global[type]; struct daemon_local_s *sl; xfree (parm); /* We have copied all data to the stack. */ -#ifdef HAVE_W32_SYSTEM - npth_unprotect (); - /* Note that although we use a pid_t here, it is actually a HANDLE. */ - WaitForSingleObject ((HANDLE)pid, INFINITE); - npth_protect (); + assuan_pipe_wait_server_termination (g->primary_ctx, NULL, 0); log_info ("daemon %s finished\n", name); -#else /* !HAVE_W32_SYSTEM*/ - - again: - npth_unprotect (); - err = waitpid (pid, &wstatus, 0); - npth_protect (); - - if (err < 0) - { - if (errno == EINTR) - goto again; - log_error ("waitpid for %s failed: %s\n", name, strerror (errno)); - return NULL; - } - else - { - if (WIFEXITED (wstatus)) - log_info ("daemon %s finished (status %d)\n", - name, WEXITSTATUS (wstatus)); - else if (WIFSIGNALED (wstatus)) - log_info ("daemon %s killed by signal %d\n", name, WTERMSIG (wstatus)); - else - { - if (WIFSTOPPED (wstatus)) - log_info ("daemon %s stopped by signal %d\n", - name, WSTOPSIG (wstatus)); - goto again; - } - - assuan_set_flag (g->primary_ctx, ASSUAN_NO_WAITPID, 1); - } -#endif /*!HAVE_W32_SYSTEM*/ agent_flush_cache (1); /* Flush the PIN cache. */ @@ -471,8 +430,8 @@ daemon_start (enum daemon_type type, ctrl_t ctrl) char buf[100]; #ifdef HAVE_W32_SYSTEM - snprintf (buf, sizeof buf, "OPTION event-signal=%lx", - (unsigned long)get_agent_daemon_notify_event ()); + snprintf (buf, sizeof buf, "OPTION event-signal=%p", + get_agent_daemon_notify_event ()); #else snprintf (buf, sizeof buf, "OPTION event-signal=%d", SIGUSR2); #endif @@ -496,7 +455,6 @@ daemon_start (enum daemon_type type, ctrl_t ctrl) } wctp->type = type; - wctp->pid = assuan_get_pid (g->primary_ctx); err = npth_attr_init (&tattr); if (!err) { @@ -561,10 +519,9 @@ agent_daemon_dump_state (void) for (i = 0; i < DAEMON_MAX_TYPE; i++) { struct daemon_global_s *g = &daemon_global[i]; - log_info ("%s: name %s primary_ctx=%p pid=%ld reusable=%d\n", __func__, + log_info ("%s: name %s primary_ctx=%p reusable=%d\n", __func__, gnupg_module_name (daemon_modules[i]), g->primary_ctx, - (long)assuan_get_pid (g->primary_ctx), g->primary_ctx_reusable); if (g->socket_name) log_info ("%s: socket='%s'\n", __func__, g->socket_name); diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index d236e1107..4a999ca9a 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -128,8 +128,9 @@ initialize_module_call_pinentry (void) void agent_query_dump_state (void) { - log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n", - entry_ctx, (long)assuan_get_pid (entry_ctx), (void*)popup_tid); + log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%lx\n", + entry_ctx, (long)assuan_get_pid (entry_ctx), + (unsigned long)popup_tid); } /* Called to make sure that a popup window owned by the current @@ -1288,8 +1289,6 @@ build_cmd_setdesc (char *line, size_t linelen, const char *desc) static void * watch_sock (void *arg) { - pid_t pid = assuan_get_pid (entry_ctx); - while (1) { int err; @@ -1302,7 +1301,7 @@ watch_sock (void *arg) FD_ZERO (&fdset); FD_SET (FD2INT (sock), &fdset); - err = npth_select (FD2INT (sock)+1, &fdset, NULL, NULL, &timeout); + err = npth_select (FD2NUM (sock)+1, &fdset, NULL, NULL, &timeout); if (err < 0) { @@ -1317,17 +1316,7 @@ watch_sock (void *arg) break; } - if (pid == (pid_t)(-1)) - ; /* No pid available can't send a kill. */ -#ifdef HAVE_W32_SYSTEM - /* Older versions of assuan set PID to 0 on Windows to indicate an - invalid value. */ - else if (pid != (pid_t) INVALID_HANDLE_VALUE && pid != 0) - TerminateProcess ((HANDLE)pid, 1); -#else - else if (pid > 0) - kill (pid, SIGINT); -#endif + assuan_pipe_kill_server (entry_ctx); return NULL; } @@ -2124,7 +2113,6 @@ void agent_popup_message_stop (ctrl_t ctrl) { int rc; - pid_t pid; (void)ctrl; @@ -2137,26 +2125,10 @@ agent_popup_message_stop (ctrl_t ctrl) return; } - pid = assuan_get_pid (entry_ctx); - if (pid == (pid_t)(-1)) - ; /* No pid available can't send a kill. */ - else if (popup_finished) + if (popup_finished) ; /* Already finished and ready for joining. */ -#ifdef HAVE_W32_SYSTEM - /* Older versions of assuan set PID to 0 on Windows to indicate an - invalid value. */ - else if (pid != (pid_t) INVALID_HANDLE_VALUE - && pid != 0) - { - HANDLE process = (HANDLE) pid; - - /* Arbitrary error code. */ - TerminateProcess (process, 1); - } -#else - else if (pid > 0) - kill (pid, SIGINT); -#endif + else + assuan_pipe_kill_server (entry_ctx); /* Now wait for the thread to terminate. */ rc = npth_join (popup_tid, NULL); diff --git a/agent/call-scd.c b/agent/call-scd.c index 91e28e68c..3da16e619 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -548,7 +548,8 @@ padding_info_cb (void *opaque, const char *line) if ((s=has_leading_keyword (line, "PADDING"))) { - *r_padding = atoi (s); + if (r_padding) + *r_padding = atoi (s); } else if ((s=has_leading_keyword (line, "PINCACHE_PUT"))) err = handle_pincache_put (s); @@ -560,8 +561,8 @@ padding_info_cb (void *opaque, const char *line) /* Decipher INDATA using the current card. Note that the returned * value is not an s-expression but the raw data as returned by * scdaemon. The padding information is stored at R_PADDING with -1 - * for not known. DESC_TEXT is an additional parameter passed to - * GETPIN_CB. */ + * for not known, when it's not NULL. DESC_TEXT is an additional + * parameter passed to GETPIN_CB. */ int agent_card_pkdecrypt (ctrl_t ctrl, const char *keyid, @@ -579,7 +580,8 @@ agent_card_pkdecrypt (ctrl_t ctrl, size_t len; *r_buf = NULL; - *r_padding = -1; /* Unknown. */ + if (r_padding) + *r_padding = -1; /* Unknown. */ rc = start_scd (ctrl); if (rc) return rc; diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 189acd7f8..02f069dea 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -3952,7 +3952,11 @@ start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client) es_syshd_t syshd; syshd.type = ES_SYSHD_SOCK; +#ifdef HAVE_SOCKET + syshd.u.sock = (SOCKET)sock_client; +#else syshd.u.sock = sock_client; +#endif get_client_info (sock_client, &peer_info); ctrl->client_pid = peer_info.pid; diff --git a/agent/command.c b/agent/command.c index 40322f385..b152a5ea4 100644 --- a/agent/command.c +++ b/agent/command.c @@ -241,7 +241,7 @@ reset_notify (assuan_context_t ctx, char *line) (void) line; memset (ctrl->keygrip, 0, 20); - ctrl->have_keygrip = 0; + ctrl->have_keygrip = ctrl->have_keygrip1 = 0; ctrl->digest.valuelen = 0; xfree (ctrl->digest.data); ctrl->digest.data = NULL; @@ -796,8 +796,8 @@ cmd_havekey (assuan_context_t ctx, char *line) static const char hlp_sigkey[] = - "SIGKEY <hexstring_with_keygrip>\n" - "SETKEY <hexstring_with_keygrip>\n" + "SIGKEY [--another] <hexstring_with_keygrip>\n" + "SETKEY [--another] <hexstring_with_keygrip>\n" "\n" "Set the key used for a sign or decrypt operation."; static gpg_error_t @@ -805,11 +805,17 @@ cmd_sigkey (assuan_context_t ctx, char *line) { int rc; ctrl_t ctrl = assuan_get_pointer (ctx); + int opt_another; - rc = parse_keygrip (ctx, line, ctrl->keygrip); + opt_another = has_option (line, "--another"); + line = skip_options (line); + rc = parse_keygrip (ctx, line, opt_another? ctrl->keygrip1 : ctrl->keygrip); if (rc) return rc; - ctrl->have_keygrip = 1; + if (opt_another) + ctrl->have_keygrip1 = 1; + else + ctrl->have_keygrip = 1; return 0; } @@ -1043,10 +1049,14 @@ cmd_pksign (assuan_context_t ctx, char *line) static const char hlp_pkdecrypt[] = - "PKDECRYPT [<options>]\n" + "PKDECRYPT [--kem[=<kemid>] [<options>]\n" "\n" "Perform the actual decrypt operation. Input is not\n" - "sensitive to eavesdropping."; + "sensitive to eavesdropping.\n" + "If the --kem option is used, decryption is done with the KEM,\n" + "inquiring upper-layer option, when needed. KEMID can be\n" + "specified with --kem option; Valid value is: PQC-PGP, PGP, or CMS.\n" + "Default is PQC-PGP."; static gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line) { @@ -1055,22 +1065,52 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) unsigned char *value; size_t valuelen; membuf_t outbuf; - int padding; + int padding = -1; + unsigned char *option = NULL; + size_t optionlen = 0; + const char *p; + int kemid = -1; - (void)line; + p = has_option_name (line, "--kem"); + if (p) + { + kemid = KEM_PQC_PGP; + if (*p == '=') + { + p++; + if (!strcmp (p, "PQC-PGP")) + kemid = KEM_PQC_PGP; + else if (!strcmp (p, "PGP")) + kemid = KEM_PGP; + else if (!strcmp (p, "CMS")) + kemid = KEM_CMS; + else + return set_error (GPG_ERR_ASS_PARAMETER, "invalid KEM algorithm"); + } + } /* First inquire the data to decrypt */ rc = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%u", MAXLEN_CIPHERTEXT); if (!rc) rc = assuan_inquire (ctx, "CIPHERTEXT", &value, &valuelen, MAXLEN_CIPHERTEXT); + if (!rc && kemid > KEM_PQC_PGP) + rc = assuan_inquire (ctx, "OPTION", + &option, &optionlen, MAXLEN_CIPHERTEXT); if (rc) return rc; init_membuf (&outbuf, 512); - rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc, - value, valuelen, &outbuf, &padding); + if (kemid < 0) + rc = agent_pkdecrypt (ctrl, ctrl->server_local->keydesc, + value, valuelen, &outbuf, &padding); + else + { + rc = agent_kem_decrypt (ctrl, ctrl->server_local->keydesc, kemid, + value, valuelen, option, optionlen, &outbuf); + xfree (option); + } xfree (value); if (rc) clear_outbuf (&outbuf); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 50755c0fd..420dbb464 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1384,6 +1384,17 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data, err = gcry_sexp_extract_param (list, NULL, format, array+0, array+1, NULL); } + else if ( !strcmp (name, (algoname = "kyber512")) + || !strcmp (name, (algoname = "kyber768")) + || !strcmp (name, (algoname = "kyber1024"))) + { + format = "/ps?"; + elems = "ps?"; + npkey = 1; + nskey = 2; + err = gcry_sexp_extract_param (list, NULL, format, + array+0, array+1, NULL); + } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); diff --git a/agent/divert-scd.c b/agent/divert-scd.c index ed0173ea1..d8c2bcca7 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -377,10 +377,10 @@ divert_pksign (ctrl_t ctrl, const unsigned char *grip, } -/* Decrypt the value given asn an S-expression in CIPHER using the +/* Decrypt the value given as an s-expression in CIPHER using the key identified by SHADOW_INFO and return the plaintext in an allocated buffer in R_BUF. The padding information is stored at - R_PADDING with -1 for not known. */ + R_PADDING with -1 for not known, when it's not NULL. */ int divert_pkdecrypt (ctrl_t ctrl, const unsigned char *grip, @@ -399,7 +399,8 @@ divert_pkdecrypt (ctrl_t ctrl, bin2hex (grip, 20, hexgrip); - *r_padding = -1; + if (r_padding) + *r_padding = -1; s = cipher; if (*s != '(') return gpg_error (GPG_ERR_INV_SEXP); @@ -485,6 +486,34 @@ divert_pkdecrypt (ctrl_t ctrl, return rc; } +gpg_error_t +agent_card_ecc_kem (ctrl_t ctrl, const unsigned char *ecc_ct, + size_t ecc_point_len, unsigned char *ecc_ecdh) +{ + gpg_error_t err = 0; + char *ecdh = NULL; + size_t len; + int rc; + + rc = agent_card_pkdecrypt (ctrl, ctrl->keygrip, getpin_cb, ctrl, NULL, + ecc_ct, ecc_point_len, &ecdh, &len, NULL); + if (rc) + return rc; + + if (len != ecc_point_len) + { + if (opt.verbose) + log_info ("%s: ECC result length invalid (%zu != %zu)\n", + __func__, len, ecc_point_len); + return gpg_error (GPG_ERR_INV_DATA); + } + else + memcpy (ecc_ecdh, ecdh, len); + + xfree (ecdh); + return err; +} + gpg_error_t divert_writekey (ctrl_t ctrl, int force, const char *serialno, diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c index 2496d091a..6ebb9ef78 100644 --- a/agent/divert-tpm2.c +++ b/agent/divert-tpm2.c @@ -106,7 +106,8 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const unsigned char *s; size_t n; - *r_padding = -1; + if (r_padding) + *r_padding = -1; s = cipher; if (*s != '(') @@ -125,7 +126,8 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, return gpg_error (GPG_ERR_INV_SEXP); if (smatch (&s, n, "rsa")) { - *r_padding = 0; + if (r_padding) + *r_padding = 0; if (*s != '(') return gpg_error (GPG_ERR_UNKNOWN_SEXP); s++; diff --git a/agent/genkey.c b/agent/genkey.c index 444f89f79..503a7eb53 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -161,7 +161,7 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN); estream_t stream_to_check_pattern = NULL; const char *argv[10]; - pid_t pid; + gnupg_process_t proc; int result, i; const char *pattern; char *patternfname; @@ -204,11 +204,17 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) argv[i] = NULL; log_assert (i < sizeof argv); - if (gnupg_spawn_process (pgmname, argv, NULL, 0, - &stream_to_check_pattern, NULL, NULL, &pid)) + if (gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDIN_PIPE, + NULL, NULL, &proc)) result = 1; /* Execute error - assume password should no be used. */ else { + int status; + + gnupg_process_get_streams (proc, 0, &stream_to_check_pattern, + NULL, NULL); + es_set_binary (stream_to_check_pattern); if (es_fwrite (pw, strlen (pw), 1, stream_to_check_pattern) != 1) { @@ -219,11 +225,13 @@ do_check_passphrase_pattern (ctrl_t ctrl, const char *pw, unsigned int flags) else es_fflush (stream_to_check_pattern); es_fclose (stream_to_check_pattern); - if (gnupg_wait_process (pgmname, pid, 1, NULL)) + gnupg_process_wait (proc, 1); + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status); + if (status) result = 1; /* Helper returned an error - probably a match. */ else result = 0; /* Success; i.e. no match. */ - gnupg_release_process (pid); + gnupg_process_release (proc); } xfree (patternfname); diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 5305098d2..3c71ba65d 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -341,15 +341,13 @@ static struct debug_flags_s debug_flags [] = #define MIN_PASSPHRASE_NONALPHA (1) #define MAX_PASSPHRASE_DAYS (0) -/* The timer tick used for housekeeping stuff. Note that on Windows - * we use a SetWaitableTimer seems to signal earlier than about 2 - * seconds. Thus we use 4 seconds on all platforms. - * CHECK_OWN_SOCKET_INTERVAL defines how often we check - * our own socket in standard socket mode. If that value is 0 we - * don't check at all. All values are in seconds. */ -#define TIMERTICK_INTERVAL (4) +/* CHECK_OWN_SOCKET_INTERVAL defines how often we check our own socket + * in standard socket mode. If that value is 0 we don't check at all. + * Values is in seconds. */ #define CHECK_OWN_SOCKET_INTERVAL (60) - +/* CHECK_PROBLEMS_INTERVAL defines how often we check the existence of + * parent process and homedir. Value is in seconds. */ +#define CHECK_PROBLEMS_INTERVAL (4) /* Flag indicating that the ssh-agent subsystem has been enabled. */ static int ssh_support; @@ -384,9 +382,6 @@ static int startup_signal_mask_valid; /* Flag to indicate that a shutdown was requested. */ static int shutdown_pending; -/* Counter for the currently running own socket checks. */ -static int check_own_socket_running; - /* Flags to indicate that check_own_socket shall not be called. */ static int disable_check_own_socket; @@ -396,6 +391,12 @@ static int is_supervised; /* Flag indicating to start the daemon even if one already runs. */ static int steal_socket; +/* Flag to monitor problems. */ +static int problem_detected; +#define AGENT_PROBLEM_SOCKET_TAKEOVER (1 << 0) +#define AGENT_PROBLEM_PARENT_HAS_GONE (1 << 1) +#define AGENT_PROBLEM_HOMEDIR_REMOVED (1 << 2) + /* Flag to inhibit socket removal in cleanup. */ static int inhibit_socket_removal; @@ -432,6 +433,17 @@ static assuan_sock_nonce_t socket_nonce_ssh; * Let's try this as default. Change at runtime with --listen-backlog. */ static int listen_backlog = 64; +#ifdef HAVE_W32_SYSTEM +/* The event to break the select call. */ +static HANDLE the_event2; +#elif defined(HAVE_PSELECT_NO_EINTR) +/* An FD to break the select call. */ +static int event_pipe_fd; +#else +/* PID of the main thread. */ +static pid_t main_thread_pid; +#endif + /* Default values for options passed to the pinentry. */ static char *default_display; static char *default_ttyname; @@ -452,9 +464,14 @@ static const char *debug_level; the log file after a SIGHUP if it didn't changed. Malloced. */ static char *current_logfile; -/* The handle_tick() function may test whether a parent is still - * running. We record the PID of the parent here or -1 if it should - * be watched. */ +#ifdef HAVE_W32_SYSTEM +#define HAVE_PARENT_PID_SUPPORT 0 +#else +#define HAVE_PARENT_PID_SUPPORT 1 +#endif +/* The check_others_thread() function may test whether a parent is + * still running. We record the PID of the parent here or -1 if it + * should be watched. */ static pid_t parent_pid = (pid_t)(-1); /* This flag is true if the inotify mechanism for detecting the @@ -462,11 +479,6 @@ static pid_t parent_pid = (pid_t)(-1); * alternative but portable stat based check. */ static int have_homedir_inotify; -/* Depending on how gpg-agent was started, the homedir inotify watch - * may not be reliable. This flag is set if we assume that inotify - * works reliable. */ -static int reliable_homedir_inotify; - /* Number of active connections. */ static int active_connections; @@ -516,13 +528,13 @@ static void agent_deinit_default_ctrl (ctrl_t ctrl); static void handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_extra, gnupg_fd_t listen_fd_browser, - gnupg_fd_t listen_fd_ssh); -static void check_own_socket (void); + gnupg_fd_t listen_fd_ssh, + int reliable_homedir_inotify); static int check_for_running_agent (int silent); - -/* Pth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - +#if CHECK_OWN_SOCKET_INTERVAL > 0 +static void *check_own_socket_thread (void *arg); +#endif +static void *check_others_thread (void *arg); /* Functions. @@ -1050,6 +1062,7 @@ thread_init_once (void) * initialized and thus Libgcrypt could not set its system call * clamp. */ gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); } @@ -1057,7 +1070,6 @@ static void initialize_modules (void) { thread_init_once (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); initialize_module_cache (); initialize_module_call_pinentry (); initialize_module_daemon (); @@ -1085,6 +1097,7 @@ main (int argc, char **argv) int gpgconf_list = 0; gpg_error_t err; struct assuan_malloc_hooks malloc_hooks; + int reliable_homedir_inotify = 1; early_system_init (); @@ -1117,7 +1130,6 @@ main (int argc, char **argv) assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); assuan_sock_init (); - assuan_sock_set_system_hooks (ASSUAN_SYSTEM_NPTH); setup_libassuan_logging (&opt.debug, NULL); setup_libgcrypt_logging (); @@ -1583,7 +1595,7 @@ main (int argc, char **argv) log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n", fd, fd_extra, fd_browser, fd_ssh); - handle_connections (fd, fd_extra, fd_browser, fd_ssh); + handle_connections (fd, fd_extra, fd_browser, fd_ssh, 1); #endif /*!HAVE_W32_SYSTEM*/ } else if (!is_daemon) @@ -1811,14 +1823,14 @@ main (int argc, char **argv) log_get_prefix (&oldflags); log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED); opt.running_detached = 1; - - /* Unless we are running with a program given on the command - * line we can assume that the inotify things works and thus - * we can avoid the regular stat calls. */ - if (!argc) - reliable_homedir_inotify = 1; } + /* When we are running with a program given on the command + * line, the inotify things may not work well and thus + * we cannot avoid the regular stat calls. */ + if (argc) + reliable_homedir_inotify = 0; + { struct sigaction sa; @@ -1837,7 +1849,8 @@ main (int argc, char **argv) } log_info ("%s %s started\n", gpgrt_strusage(11), gpgrt_strusage(13) ); - handle_connections (fd, fd_extra, fd_browser, fd_ssh); + handle_connections (fd, fd_extra, fd_browser, fd_ssh, + reliable_homedir_inotify); assuan_sock_close (fd); } @@ -2139,39 +2152,45 @@ get_agent_active_connection_count (void) notification event. Calling it the first time creates that event. */ #if defined(HAVE_W32_SYSTEM) +static void * +create_an_event (void) +{ + HANDLE h, h2; + SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; + + /* We need to use a manual reset event object due to the way our + w32-pth wait function works: If we would use an automatic + reset event we are not able to figure out which handle has + been signaled because at the time we single out the signaled + handles using WFSO the event has already been reset due to + the WFMO. */ + h = CreateEvent (&sa, TRUE, FALSE, NULL); + if (!h) + log_error ("can't create an event: %s\n", w32_strerror (-1) ); + else if (!DuplicateHandle (GetCurrentProcess(), h, + GetCurrentProcess(), &h2, + EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) + { + log_error ("setting synchronize for an event failed: %s\n", + w32_strerror (-1) ); + CloseHandle (h); + } + else + { + CloseHandle (h); + return h2; + } + + return INVALID_HANDLE_VALUE; +} + void * get_agent_daemon_notify_event (void) { static HANDLE the_event = INVALID_HANDLE_VALUE; if (the_event == INVALID_HANDLE_VALUE) - { - HANDLE h, h2; - SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; - - /* We need to use a manual reset event object due to the way our - w32-pth wait function works: If we would use an automatic - reset event we are not able to figure out which handle has - been signaled because at the time we single out the signaled - handles using WFSO the event has already been reset due to - the WFMO. */ - h = CreateEvent (&sa, TRUE, FALSE, NULL); - if (!h) - log_error ("can't create scd notify event: %s\n", w32_strerror (-1) ); - else if (!DuplicateHandle (GetCurrentProcess(), h, - GetCurrentProcess(), &h2, - EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) - { - log_error ("setting synchronize for scd notify event failed: %s\n", - w32_strerror (-1) ); - CloseHandle (h); - } - else - { - CloseHandle (h); - the_event = h2; - } - } + the_event = create_an_event (); return the_event; } @@ -2428,57 +2447,6 @@ create_directories (void) } - -/* This is the worker for the ticker. It is called every few seconds - and may only do fast operations. */ -static void -handle_tick (void) -{ - static time_t last_minute; - struct stat statbuf; - - if (!last_minute) - last_minute = time (NULL); - - /* If we are running as a child of another process, check whether - the parent is still alive and shutdown if not. */ -#ifndef HAVE_W32_SYSTEM - if (parent_pid != (pid_t)(-1)) - { - if (kill (parent_pid, 0)) - { - shutdown_pending = 2; - log_info ("parent process died - shutting down\n"); - log_info ("%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13)); - cleanup (); - agent_exit (0); - } - } -#endif /*HAVE_W32_SYSTEM*/ - - /* Code to be run from time to time. */ -#if CHECK_OWN_SOCKET_INTERVAL > 0 - if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL)) - { - check_own_socket (); - last_minute = time (NULL); - } -#endif - - /* Need to check for expired cache entries. */ - agent_cache_housekeeping (); - - /* Check whether the homedir is still available. */ - if (!shutdown_pending - && (!have_homedir_inotify || !reliable_homedir_inotify) - && gnupg_stat (gnupg_homedir (), &statbuf) && errno == ENOENT) - { - shutdown_pending = 1; - log_info ("homedir has been removed - shutting down\n"); - } -} - - /* A global function which allows us to call the reload stuff from other places too. This is only used when build for W32. */ void @@ -2537,6 +2505,11 @@ handle_signal (int signo) agent_sigusr2_action (); break; + case SIGCONT: + /* Do nothing, but break the syscall. */ + log_debug ("SIGCONT received - breaking select\n"); + break; + case SIGTERM: if (!shutdown_pending) log_info ("SIGTERM received - shutting down ...\n"); @@ -2574,7 +2547,7 @@ check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce) if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce)) { log_info (_("error reading nonce on fd %d: %s\n"), - FD2INT(ctrl->thread_startup.fd), strerror (errno)); + FD_DBG (ctrl->thread_startup.fd), strerror (errno)); assuan_sock_close (ctrl->thread_startup.fd); xfree (ctrl); return -1; @@ -2874,12 +2847,12 @@ do_start_connection_thread (ctrl_t ctrl) agent_init_default_ctrl (ctrl); if (opt.verbose > 1 && !DBG_IPC) log_info (_("handler 0x%lx for fd %d started\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); start_command_handler (ctrl, GNUPG_INVALID_FD, ctrl->thread_startup.fd); if (opt.verbose > 1 && !DBG_IPC) log_info (_("handler 0x%lx for fd %d terminated\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); agent_deinit_default_ctrl (ctrl); xfree (ctrl); @@ -2954,12 +2927,12 @@ start_connection_thread_ssh (void *arg) agent_init_default_ctrl (ctrl); if (opt.verbose) log_info (_("ssh handler 0x%lx for fd %d started\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); start_command_handler_ssh (ctrl, ctrl->thread_startup.fd); if (opt.verbose) log_info (_("ssh handler 0x%lx for fd %d terminated\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); agent_deinit_default_ctrl (ctrl); xfree (ctrl); @@ -2968,13 +2941,36 @@ start_connection_thread_ssh (void *arg) } +void +agent_kick_the_loop (void) +{ + /* Kick the select loop. */ +#ifdef HAVE_W32_SYSTEM + int ret = SetEvent (the_event2); + if (ret == 0) + log_error ("SetEvent for agent_kick_the_loop failed: %s\n", + w32_strerror (-1)); +#else +# ifdef HAVE_PSELECT_NO_EINTR + write (event_pipe_fd, "", 1); +# else + int ret = kill (main_thread_pid, SIGCONT); + if (ret < 0) + log_error ("sending signal for agent_kick_the_loop failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); +# endif +#endif +} + + /* Connection handler loop. Wait for connection requests and spawn a thread after accepting a connection. */ static void handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t listen_fd_extra, gnupg_fd_t listen_fd_browser, - gnupg_fd_t listen_fd_ssh) + gnupg_fd_t listen_fd_ssh, + int reliable_homedir_inotify) { gpg_error_t err; npth_attr_t tattr; @@ -2985,12 +2981,15 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_fd_t fd; int nfd; int saved_errno; - struct timespec abstime; - struct timespec curtime; - struct timespec timeout; + struct timespec *tp; #ifdef HAVE_W32_SYSTEM - HANDLE events[2]; + HANDLE events[3]; unsigned int events_set; +#else + int signo; +# ifdef HAVE_PSELECT_NO_EINTR + int pipe_fd[2]; +# endif #endif int sock_inotify_fd = -1; int home_inotify_fd = -1; @@ -3018,11 +3017,24 @@ handle_connections (gnupg_fd_t listen_fd, npth_sigev_add (SIGUSR1); npth_sigev_add (SIGUSR2); npth_sigev_add (SIGINT); + npth_sigev_add (SIGCONT); npth_sigev_add (SIGTERM); npth_sigev_fini (); +# ifdef HAVE_PSELECT_NO_EINTR + ret = gnupg_create_pipe (pipe_fd); + if (ret) + { + log_error ("pipe creation failed: %s\n", gpg_strerror (ret)); + return; + } + event_pipe_fd = pipe_fd[1]; +# else + main_thread_pid = getpid (); +# endif #else events[0] = get_agent_daemon_notify_event (); - events[1] = INVALID_HANDLE_VALUE; + events[1] = the_event2 = create_an_event (); + events[2] = INVALID_HANDLE_VALUE; #endif if (disable_check_own_socket) @@ -3034,7 +3046,7 @@ handle_connections (gnupg_fd_t listen_fd, gpg_strerror (err)); } - if (disable_check_own_socket) + if (!reliable_homedir_inotify) home_inotify_fd = -1; else if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd, gnupg_homedir ()))) @@ -3046,6 +3058,27 @@ handle_connections (gnupg_fd_t listen_fd, else have_homedir_inotify = 1; +#if CHECK_OWN_SOCKET_INTERVAL > 0 + if (!disable_check_own_socket && sock_inotify_fd == -1) + { + npth_t thread; + + err = npth_create (&thread, &tattr, check_own_socket_thread, NULL); + if (err) + log_error ("error spawning check_own_socket_thread: %s\n", strerror (err)); + } +#endif + + if ((HAVE_PARENT_PID_SUPPORT && parent_pid != (pid_t)(-1)) + || !have_homedir_inotify) + { + npth_t thread; + + err = npth_create (&thread, &tattr, check_others_thread, NULL); + if (err) + log_error ("error spawning check_others_thread: %s\n", strerror (err)); + } + /* On Windows we need to fire up a separate thread to listen for requests from Putty (an SSH client), so we can replace Putty's Pageant (its ssh-agent implementation). */ @@ -3075,24 +3108,24 @@ handle_connections (gnupg_fd_t listen_fd, FD_ZERO (&fdset); FD_SET (FD2INT (listen_fd), &fdset); - nfd = FD2INT (listen_fd); + nfd = FD2NUM (listen_fd); if (listen_fd_extra != GNUPG_INVALID_FD) { FD_SET ( FD2INT(listen_fd_extra), &fdset); if (FD2INT (listen_fd_extra) > nfd) - nfd = FD2INT (listen_fd_extra); + nfd = FD2NUM (listen_fd_extra); } if (listen_fd_browser != GNUPG_INVALID_FD) { FD_SET ( FD2INT(listen_fd_browser), &fdset); if (FD2INT (listen_fd_browser) > nfd) - nfd = FD2INT (listen_fd_browser); + nfd = FD2NUM (listen_fd_browser); } if (listen_fd_ssh != GNUPG_INVALID_FD) { FD_SET ( FD2INT(listen_fd_ssh), &fdset); if (FD2INT (listen_fd_ssh) > nfd) - nfd = FD2INT (listen_fd_ssh); + nfd = FD2NUM (listen_fd_ssh); } if (sock_inotify_fd != -1) { @@ -3112,15 +3145,12 @@ handle_connections (gnupg_fd_t listen_fd, listentbl[2].l_fd = listen_fd_browser; listentbl[3].l_fd = listen_fd_ssh; - npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; - for (;;) { /* Shutdown test. */ if (shutdown_pending) { - if (active_connections == 0) + if (active_connections == 0 || is_supervised) break; /* ready */ /* Do not accept new connections but keep on running the @@ -3149,28 +3179,23 @@ handle_connections (gnupg_fd_t listen_fd, thus a simple assignment is fine to copy the entire set. */ read_fdset = fdset; - npth_clock_gettime (&curtime); - if (!(npth_timercmp (&curtime, &abstime, <))) - { - /* Timeout. */ - handle_tick (); - npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; - } - npth_timersub (&abstime, &curtime, &timeout); +#ifdef HAVE_PSELECT_NO_EINTR + FD_SET (pipe_fd[0], &read_fdset); + if (nfd < pipe_fd[0]) + nfd = pipe_fd[0]; +#endif + + tp = agent_cache_expiration (); #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, tp, npth_sigev_sigmask ()); saved_errno = errno; - { - int signo; - while (npth_sigev_get_pending (&signo)) - handle_signal (signo); - } + while (npth_sigev_get_pending (&signo)) + handle_signal (signo); #else - ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, tp, events, &events_set); saved_errno = errno; @@ -3186,11 +3211,47 @@ handle_connections (gnupg_fd_t listen_fd, gnupg_sleep (1); continue; } + +#ifndef HAVE_W32_SYSTEM + if ((problem_detected & AGENT_PROBLEM_PARENT_HAS_GONE)) + { + shutdown_pending = 2; + log_info ("parent process died - shutting down\n"); + log_info ("%s %s stopped\n", gpgrt_strusage(11), gpgrt_strusage(13)); + cleanup (); + agent_exit (0); + } +#endif + + if ((problem_detected & AGENT_PROBLEM_SOCKET_TAKEOVER)) + { + /* We may not remove the socket as it is now in use by another + server. */ + inhibit_socket_removal = 1; + shutdown_pending = 2; + log_info ("this process is useless - shutting down\n"); + } + + if ((problem_detected & AGENT_PROBLEM_HOMEDIR_REMOVED)) + { + shutdown_pending = 1; + log_info ("homedir has been removed - shutting down\n"); + } + if (ret <= 0) /* Interrupt or timeout. Will be handled when calculating the next timeout. */ continue; +#ifdef HAVE_PSELECT_NO_EINTR + if (FD_ISSET (pipe_fd[0], &read_fdset)) + { + char buf[256]; + + read (pipe_fd[0], buf, sizeof buf); + } +#endif + /* The inotify fds are set even when a shutdown is pending (see * above). So we must handle them in any case. To avoid that * they trigger a second time we close them immediately. */ @@ -3198,7 +3259,10 @@ handle_connections (gnupg_fd_t listen_fd, && FD_ISSET (sock_inotify_fd, &read_fdset) && gnupg_inotify_has_name (sock_inotify_fd, GPG_AGENT_SOCK_NAME)) { - shutdown_pending = 1; + /* We may not remove the socket (if any), as it may be now + in use by another server. */ + inhibit_socket_removal = 1; + shutdown_pending = 2; close (sock_inotify_fd); sock_inotify_fd = -1; log_info ("socket file has been removed - shutting down\n"); @@ -3227,8 +3291,8 @@ handle_connections (gnupg_fd_t listen_fd, continue; plen = sizeof paddr; - fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd), - (struct sockaddr *)&paddr, &plen)); + fd = assuan_sock_accept (listentbl[idx].l_fd, + (struct sockaddr *)&paddr, &plen); if (fd == GNUPG_INVALID_FD) { log_error ("accept failed for %s: %s\n", @@ -3268,13 +3332,21 @@ handle_connections (gnupg_fd_t listen_fd, close (sock_inotify_fd); if (home_inotify_fd != -1) close (home_inotify_fd); +#ifdef HAVE_W32_SYSTEM + if (the_event2 != INVALID_HANDLE_VALUE) + CloseHandle (the_event2); +#endif +#ifdef HAVE_PSELECT_NO_EINTR + close (pipe_fd[0]); + close (pipe_fd[1]); +#endif cleanup (); log_info (_("%s %s stopped\n"), gpgrt_strusage(11), gpgrt_strusage(13)); npth_attr_destroy (&tattr); } - +#if CHECK_OWN_SOCKET_INTERVAL > 0 /* Helper for check_own_socket. */ static gpg_error_t check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length) @@ -3285,20 +3357,18 @@ check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length) } -/* The thread running the actual check. We need to run this in a - separate thread so that check_own_thread can be called from the - timer tick. */ -static void * -check_own_socket_thread (void *arg) +/* Check whether we are still listening on our own socket. In case + another gpg-agent process started after us has taken ownership of + our socket, we would linger around without any real task. Thus we + better check once in a while whether we are really needed. */ +static int +do_check_own_socket (const char *sockname) { int rc; - char *sockname = arg; assuan_context_t ctx = NULL; membuf_t mb; char *buffer; - check_own_socket_running++; - rc = assuan_new (&ctx); if (rc) { @@ -3336,58 +3406,78 @@ check_own_socket_thread (void *arg) xfree (buffer); leave: - xfree (sockname); if (ctx) assuan_release (ctx); - if (rc) - { - /* We may not remove the socket as it is now in use by another - server. */ - inhibit_socket_removal = 1; - shutdown_pending = 2; - log_info ("this process is useless - shutting down\n"); - } - check_own_socket_running--; - return NULL; -} + return rc; +} -/* Check whether we are still listening on our own socket. In case - another gpg-agent process started after us has taken ownership of - our socket, we would linger around without any real task. Thus we - better check once in a while whether we are really needed. */ -static void -check_own_socket (void) +/* The thread running the actual check. */ +static void * +check_own_socket_thread (void *arg) { char *sockname; - npth_t thread; - npth_attr_t tattr; - int err; - - if (disable_check_own_socket) - return; - if (check_own_socket_running || shutdown_pending) - return; /* Still running or already shutting down. */ + (void)arg; sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); if (!sockname) - return; /* Out of memory. */ + return NULL; /* Out of memory. */ - err = npth_attr_init (&tattr); - if (err) + while (!problem_detected) { - xfree (sockname); - return; + if (shutdown_pending) + goto leave; + + gnupg_sleep (CHECK_OWN_SOCKET_INTERVAL); + + if (do_check_own_socket (sockname)) + problem_detected |= AGENT_PROBLEM_SOCKET_TAKEOVER; } - npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); - err = npth_create (&thread, &tattr, check_own_socket_thread, sockname); - if (err) - log_error ("error spawning check_own_socket_thread: %s\n", strerror (err)); - npth_attr_destroy (&tattr); + + agent_kick_the_loop (); + + leave: + xfree (sockname); + return NULL; } +#endif + +/* The thread running other checks. */ +static void * +check_others_thread (void *arg) +{ + const char *homedir = gnupg_homedir (); + + (void)arg; + + while (!problem_detected) + { + struct stat statbuf; + + if (shutdown_pending) + goto leave; + + gnupg_sleep (CHECK_PROBLEMS_INTERVAL); + + /* If we are running as a child of another process, check whether + the parent is still alive and shutdown if not. */ +#ifndef HAVE_W32_SYSTEM + if (parent_pid != (pid_t)(-1) && kill (parent_pid, 0)) + problem_detected |= AGENT_PROBLEM_PARENT_HAS_GONE; +#endif /*HAVE_W32_SYSTEM*/ + + /* Check whether the homedir is still available. */ + if (!have_homedir_inotify + && gnupg_stat (homedir, &statbuf) && errno == ENOENT) + problem_detected |= AGENT_PROBLEM_HOMEDIR_REMOVED; + } + agent_kick_the_loop (); + leave: + return NULL; +} /* Figure out whether an agent is available and running. Prints an error if not. If SILENT is true, no messages are printed. diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index c26f21d35..efaf53098 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -27,8 +27,83 @@ #include <sys/stat.h> #include "agent.h" +#include "../common/openpgpdefs.h" +/* Table with parameters for KEM decryption. Use get_ecc_parms to + * find an entry. */ +struct ecc_params +{ + const char *curve; /* Canonical name of the curve. */ + size_t pubkey_len; /* Pubkey in the SEXP representation. */ + size_t scalar_len; + size_t point_len; + size_t shared_len; + int hash_algo; + int kem_algo; + int scalar_reverse; +}; + +static const struct ecc_params ecc_table[] = + { + { + "Curve25519", + 33, 32, 32, 32, + GCRY_MD_SHA3_256, GCRY_KEM_RAW_X25519, + 1 + }, + { + "X448", + 56, 56, 56, 64, + GCRY_MD_SHA3_512, GCRY_KEM_RAW_X448, + 0 + }, + { + "brainpoolP256r1", + 65, 32, 65, 32, + GCRY_MD_SHA3_256, GCRY_KEM_RAW_BP256, + 0 + }, + { + "brainpoolP384r1", + 97, 48, 97, 64, + GCRY_MD_SHA3_512, GCRY_KEM_RAW_BP384, + 0 + }, + { + "brainpoolP512r1", + 129, 64, 129, 64, + GCRY_MD_SHA3_512, GCRY_KEM_RAW_BP512, + 0 + }, + { NULL, 0, 0, 0, 0, 0, 0, 0 } +}; + + +/* Maximum buffer sizes required for ECC KEM. Keep this aligned to + * the ecc_table above. */ +#define ECC_SCALAR_LEN_MAX 64 +#define ECC_POINT_LEN_MAX (1+2*64) +#define ECC_HASH_LEN_MAX 64 + + + +/* Return the ECC parameters for CURVE. CURVE is expected to be the + * canonical name. */ +static const struct ecc_params * +get_ecc_params (const char *curve) +{ + int i; + + for (i = 0; ecc_table[i].curve; i++) + if (!strcmp (ecc_table[i].curve, curve)) + return &ecc_table[i]; + + return NULL; +} + + + /* DECRYPT the stuff in ciphertext which is expected to be a S-Exp. Try to get the key from CTRL and write the decoded stuff back to OUTFP. The padding information is stored at R_PADDING with -1 @@ -41,7 +116,6 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, gcry_sexp_t s_skey = NULL, s_cipher = NULL, s_plain = NULL; unsigned char *shadow_info = NULL; gpg_error_t err = 0; - int no_shadow_info = 0; char *buf = NULL; size_t len; @@ -70,17 +144,13 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, err = agent_key_from_file (ctrl, NULL, desc_text, NULL, &shadow_info, CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL); - if (gpg_err_code (err) == GPG_ERR_NO_SECKEY) - no_shadow_info = 1; - else if (err) + if (err && gpg_err_code (err) != GPG_ERR_NO_SECKEY) { log_error ("failed to read the secret key\n"); - goto leave; } - - if (shadow_info || no_shadow_info) + else if (shadow_info + || err /* gpg_err_code (err) == GPG_ERR_NO_SECKEY */) { /* divert operation to the smartcard */ - if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL)) { err = gpg_error (GPG_ERR_INV_SEXP); @@ -95,12 +165,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, &buf, &len, r_padding); if (err) { - /* We restore the original error (ie. no seckey) is no card + /* We restore the original error (ie. no seckey) as no card * has been found and we have no shadow key. This avoids a * surprising "card removed" error code. */ if ((gpg_err_code (err) == GPG_ERR_CARD_REMOVED || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) - && no_shadow_info) + && !shadow_info) err = gpg_error (GPG_ERR_NO_SECKEY); else log_error ("smartcard decryption failed: %s\n", gpg_strerror (err)); @@ -157,3 +227,597 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, xfree (shadow_info); return err; } + + +/* Reverse BUFFER to change the endianness. */ +static void +reverse_buffer (unsigned char *buffer, unsigned int length) +{ + unsigned int tmp, i; + + for (i=0; i < length/2; i++) + { + tmp = buffer[i]; + buffer[i] = buffer[length-1-i]; + buffer[length-1-i] = tmp; + } +} + + +static gpg_error_t +ecc_extract_pk_from_key (const struct ecc_params *ecc, gcry_sexp_t s_skey, + unsigned char *ecc_pk) +{ + gpg_error_t err; + unsigned int nbits; + const unsigned char *p; + size_t len; + gcry_mpi_t ecc_pk_mpi = NULL; + + err = gcry_sexp_extract_param (s_skey, NULL, "/q", &ecc_pk_mpi, NULL); + if (err) + { + if (opt.verbose) + log_info ("%s: extracting q and d from ECC key failed\n", __func__); + return err; + } + + p = gcry_mpi_get_opaque (ecc_pk_mpi, &nbits); + len = (nbits+7)/8; + if (len != ecc->pubkey_len) + { + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", __func__, len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + else if (len == ecc->point_len) + memcpy (ecc_pk, p, ecc->point_len); + else if (len == ecc->point_len + 1 && p[0] == 0x40) + /* Remove the 0x40 prefix (for Curve25519) */ + memcpy (ecc_pk, p+1, ecc->point_len); + else + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (ecc_pk, ecc->pubkey_len, "ECC pubkey:"); + + leave: + mpi_release (ecc_pk_mpi); + return err; +} + +static gpg_error_t +ecc_extract_sk_from_key (const struct ecc_params *ecc, gcry_sexp_t s_skey, + unsigned char *ecc_sk) +{ + gpg_error_t err; + unsigned int nbits; + const unsigned char *p; + size_t len; + gcry_mpi_t ecc_sk_mpi = NULL; + + err = gcry_sexp_extract_param (s_skey, NULL, "/d", &ecc_sk_mpi, NULL); + if (err) + { + if (opt.verbose) + log_info ("%s: extracting d from ECC key failed\n", __func__); + return err; + } + + p = gcry_mpi_get_opaque (ecc_sk_mpi, &nbits); + len = (nbits+7)/8; + if (len > ecc->scalar_len) + { + if (opt.verbose) + log_info ("%s: ECC secret key too long (%zu)\n", __func__, len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + memset (ecc_sk, 0, ecc->scalar_len - len); + memcpy (ecc_sk + ecc->scalar_len - len, p, len); + if (ecc->scalar_reverse) + reverse_buffer (ecc_sk, ecc->scalar_len); + mpi_release (ecc_sk_mpi); + ecc_sk_mpi = NULL; + + if (DBG_CRYPTO) + log_printhex (ecc_sk, ecc->scalar_len, "ECC seckey:"); + + leave: + mpi_release (ecc_sk_mpi); + return err; +} + +static gpg_error_t +ecc_raw_kem (const struct ecc_params *ecc, gcry_sexp_t s_skey, + const unsigned char *ecc_ct, unsigned char *ecc_ecdh) +{ + gpg_error_t err = 0; + unsigned char ecc_sk[ECC_SCALAR_LEN_MAX]; + + if (ecc->scalar_len > ECC_SCALAR_LEN_MAX) + { + if (opt.verbose) + log_info ("%s: ECC scalar length invalid (%zu)\n", + __func__, ecc->scalar_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + err = ecc_extract_sk_from_key (ecc, s_skey, ecc_sk); + if (err) + goto leave; + + err = gcry_kem_decap (ecc->kem_algo, ecc_sk, ecc->scalar_len, + ecc_ct, ecc->point_len, ecc_ecdh, ecc->point_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_decap for ECC failed\n", __func__); + } + + leave: + wipememory (ecc_sk, sizeof ecc_sk); + + return err; +} + +static gpg_error_t +get_cardkey (ctrl_t ctrl, const char *keygrip, gcry_sexp_t *r_s_pk) +{ + gpg_error_t err; + unsigned char *pkbuf; + size_t pkbuflen; + + err = agent_card_readkey (ctrl, keygrip, &pkbuf, NULL); + if (err) + return err; + + pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL); + err = gcry_sexp_sscan (r_s_pk, NULL, (char*)pkbuf, pkbuflen); + if (err) + log_error ("failed to build S-Exp from received card key: %s\n", + gpg_strerror (err)); + + xfree (pkbuf); + return err; +} + +static gpg_error_t +ecc_get_curve (ctrl_t ctrl, gcry_sexp_t s_skey, const char **r_curve) +{ + gpg_error_t err = 0; + gcry_sexp_t s_skey_card = NULL; + const char *curve = NULL; + gcry_sexp_t key; + + *r_curve = NULL; + + if (!s_skey) + { + err = get_cardkey (ctrl, ctrl->keygrip, &s_skey_card); + if (err) + goto leave; + + key = s_skey_card; + } + else + key = s_skey; + + curve = get_ecc_curve_from_key (key); + if (!curve) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + *r_curve = curve; + + leave: + gcry_sexp_release (s_skey_card); + return err; +} + +/* Given a private key in SEXP by S_SKEY0 and a cipher text by ECC_CT + * with length ECC_POINT_LEN, do ECC-KEM operation. Result is + * returned in the memory referred by ECC_SS. Shared secret length is + * returned in the memory referred by R_SHARED_LEN. CTRL is used to + * access smartcard, internally. */ +static gpg_error_t +ecc_pgp_kem_decrypt (ctrl_t ctrl, gcry_sexp_t s_skey0, + unsigned char *shadow_info0, + const unsigned char *ecc_ct, size_t ecc_point_len, + unsigned char *ecc_ss, size_t *r_shared_len) +{ + gpg_error_t err; + unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; + unsigned char ecc_pk[ECC_POINT_LEN_MAX]; + const char *curve; + const struct ecc_params *ecc = NULL; + + if (ecc_point_len > ECC_POINT_LEN_MAX) + return gpg_error (GPG_ERR_INV_DATA); + + err = ecc_get_curve (ctrl, s_skey0, &curve); + if (err) + { + if ((gpg_err_code (err) == GPG_ERR_CARD_REMOVED + || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) + && !s_skey0) + err = gpg_error (GPG_ERR_NO_SECKEY); + return err; + } + + ecc = get_ecc_params (curve); + if (!ecc) + { + if (opt.verbose) + log_info ("%s: curve '%s' not supported\n", __func__, curve); + return gpg_error (GPG_ERR_BAD_SECKEY); + } + + *r_shared_len = ecc->shared_len; + + if (DBG_CRYPTO) + log_debug ("ECC curve: %s\n", curve); + + if (ecc->point_len != ecc_point_len) + { + if (opt.verbose) + log_info ("%s: ECC cipher text length invalid (%zu != %zu)\n", + __func__, ecc->point_len, ecc_point_len); + return gpg_error (GPG_ERR_INV_DATA); + } + + err = ecc_extract_pk_from_key (ecc, s_skey0, ecc_pk); + if (err) + return err; + + if (DBG_CRYPTO) + log_printhex (ecc_ct, ecc->point_len, "ECC ephem:"); + + if (shadow_info0 || !s_skey0) + { + if (s_skey0 && agent_is_tpm2_key (s_skey0)) + { + log_error ("TPM decryption failed: %s\n", gpg_strerror (err)); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + else + { + err = agent_card_ecc_kem (ctrl, ecc_ct, ecc->point_len, ecc_ecdh); + if (err) + { + log_error ("smartcard decryption failed: %s\n", + gpg_strerror (err)); + return err; + } + } + } + else + err = ecc_raw_kem (ecc, s_skey0, ecc_ct, ecc_ecdh); + + if (err) + return err; + + if (DBG_CRYPTO) + log_printhex (ecc_ecdh, ecc_point_len, "ECC ecdh:"); + + err = gnupg_ecc_kem_kdf (ecc_ss, ecc->shared_len, ecc->hash_algo, + ecc_ecdh, ecc->point_len, ecc_ct, ecc->point_len, + ecc_pk, ecc->point_len); + + wipememory (ecc_ecdh, sizeof ecc_ecdh); + + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + return err; + } + + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc->shared_len, "ECC shared:"); + + return 0; +} + +/* For composite PGP KEM (ECC+ML-KEM), decrypt CIPHERTEXT using KEM API. + First keygrip is for ECC, second keygrip is for PQC. CIPHERTEXT + should follow the format of: + + (enc-val(pqc(c%d)(e%m)(k%m)(s%m)(fixed-info&))) + c: cipher identifier (symmetric) + e: ECDH ciphertext + k: ML-KEM ciphertext + s: encrypted session key + fixed-info: A buffer with the fixed info. + + FIXME: For now, possible keys on smartcard are not supported. + */ +static gpg_error_t +composite_pgp_kem_decrypt (ctrl_t ctrl, const char *desc_text, + gcry_sexp_t s_cipher, membuf_t *outbuf) +{ + gcry_sexp_t s_skey0 = NULL; + gcry_sexp_t s_skey1 = NULL; + unsigned char *shadow_info0 = NULL; + unsigned char *shadow_info1 = NULL; + gpg_error_t err = 0; + + unsigned int nbits; + size_t len; + + int algo; + gcry_mpi_t encrypted_sessionkey_mpi = NULL; + const unsigned char *encrypted_sessionkey; + size_t encrypted_sessionkey_len; + + gcry_mpi_t ecc_ct_mpi = NULL; + const unsigned char *ecc_ct; + size_t ecc_ct_len; + unsigned char ecc_ss[ECC_HASH_LEN_MAX]; + size_t ecc_shared_len, ecc_point_len; + + enum gcry_kem_algos mlkem_kem_algo; + gcry_mpi_t mlkem_sk_mpi = NULL; + gcry_mpi_t mlkem_ct_mpi = NULL; + const unsigned char *mlkem_sk; + size_t mlkem_sk_len; + const unsigned char *mlkem_ct; + size_t mlkem_ct_len; + unsigned char mlkem_ss[GCRY_KEM_MLKEM1024_SHARED_LEN]; + size_t mlkem_ss_len; + + unsigned char kek[32]; + size_t kek_len = 32; /* AES-256 is mandatory */ + + gcry_cipher_hd_t hd; + unsigned char sessionkey[256]; + size_t sessionkey_len; + gcry_buffer_t fixed_info = { 0, 0, 0, NULL }; + + err = agent_key_from_file (ctrl, NULL, desc_text, + ctrl->keygrip, &shadow_info0, + CACHE_MODE_NORMAL, NULL, &s_skey0, NULL, NULL); + if (err && gpg_err_code (err) != GPG_ERR_NO_SECKEY) + { + log_error ("failed to read the secret key\n"); + goto leave; + } + + err = agent_key_from_file (ctrl, NULL, desc_text, + ctrl->keygrip1, &shadow_info1, + CACHE_MODE_NORMAL, NULL, &s_skey1, NULL, NULL); + /* Here assumes no smartcard for ML-KEM, but private key in a file. */ + if (err) + { + log_error ("failed to read the another secret key\n"); + goto leave; + } + + err = gcry_sexp_extract_param (s_cipher, NULL, "%dc/eks&'fixed-info'", + &algo, &ecc_ct_mpi, &mlkem_ct_mpi, + &encrypted_sessionkey_mpi, &fixed_info, NULL); + if (err) + { + if (opt.verbose) + log_info ("%s: extracting parameters failed\n", __func__); + goto leave; + } + + ecc_ct = gcry_mpi_get_opaque (ecc_ct_mpi, &nbits); + ecc_ct_len = (nbits+7)/8; + + len = gcry_cipher_get_algo_keylen (algo); + encrypted_sessionkey = gcry_mpi_get_opaque (encrypted_sessionkey_mpi, &nbits); + encrypted_sessionkey_len = (nbits+7)/8; + if (len == 0 || encrypted_sessionkey_len != len + 8) + { + if (opt.verbose) + log_info ("%s: encrypted session key length %zu" + " does not match the length for algo %d\n", + __func__, encrypted_sessionkey_len, algo); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + /* Firstly, ECC part. */ + ecc_point_len = ecc_ct_len; + err = ecc_pgp_kem_decrypt (ctrl, s_skey0, shadow_info0, ecc_ct, ecc_point_len, + ecc_ss, &ecc_shared_len); + if (err) + goto leave; + + /* Secondly, PQC part. For now, we assume ML-KEM. */ + err = gcry_sexp_extract_param (s_skey1, NULL, "/s", &mlkem_sk_mpi, NULL); + if (err) + { + if (opt.verbose) + log_info ("%s: extracting s from PQ key failed\n", __func__); + goto leave; + } + mlkem_sk = gcry_mpi_get_opaque (mlkem_sk_mpi, &nbits); + mlkem_sk_len = (nbits+7)/8; + if (mlkem_sk_len == GCRY_KEM_MLKEM512_SECKEY_LEN) + { + mlkem_kem_algo = GCRY_KEM_MLKEM512; + mlkem_ss_len = GCRY_KEM_MLKEM512_SHARED_LEN; + mlkem_ct_len = GCRY_KEM_MLKEM512_CIPHER_LEN; + } + else if (mlkem_sk_len == GCRY_KEM_MLKEM768_SECKEY_LEN) + { + mlkem_kem_algo = GCRY_KEM_MLKEM768; + mlkem_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; + mlkem_ct_len = GCRY_KEM_MLKEM768_CIPHER_LEN; + } + else if (mlkem_sk_len == GCRY_KEM_MLKEM1024_SECKEY_LEN) + { + mlkem_kem_algo = GCRY_KEM_MLKEM1024; + mlkem_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; + mlkem_ct_len = GCRY_KEM_MLKEM1024_CIPHER_LEN; + } + else + { + if (opt.verbose) + log_info ("%s: PQ key length invalid (%zu)\n", __func__, mlkem_sk_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + mlkem_ct = gcry_mpi_get_opaque (mlkem_ct_mpi, &nbits); + len = (nbits+7)/8; + if (len != mlkem_ct_len) + { + if (opt.verbose) + log_info ("%s: PQ cipher text length invalid (%zu)\n", + __func__, mlkem_ct_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + err = gcry_kem_decap (mlkem_kem_algo, mlkem_sk, mlkem_sk_len, + mlkem_ct, mlkem_ct_len, mlkem_ss, mlkem_ss_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_decap for PQ failed\n", __func__); + goto leave; + } + + mpi_release (mlkem_sk_mpi); + mlkem_sk_mpi = NULL; + + /* Then, combine two shared secrets and ciphertexts into one KEK */ + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_shared_len, ecc_ct, ecc_point_len, + mlkem_ss, mlkem_ss_len, mlkem_ct, mlkem_ct_len, + fixed_info.data, fixed_info.size); + if (err) + { + if (opt.verbose) + log_info ("%s: KEM combiner failed\n", __func__); + goto leave; + } + + mpi_release (ecc_ct_mpi); + ecc_ct_mpi = NULL; + mpi_release (mlkem_ct_mpi); + mlkem_ct_mpi = NULL; + + if (DBG_CRYPTO) + { + log_printhex (kek, kek_len, "KEK key: "); + } + + err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + { + if (opt.verbose) + log_error ("ecdh failed to initialize AESWRAP: %s\n", + gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_setkey (hd, kek, kek_len); + + sessionkey_len = encrypted_sessionkey_len - 8; + err = gcry_cipher_decrypt (hd, sessionkey, sessionkey_len, + encrypted_sessionkey, encrypted_sessionkey_len); + gcry_cipher_close (hd); + + mpi_release (encrypted_sessionkey_mpi); + encrypted_sessionkey_mpi = NULL; + + if (err) + { + log_error ("KEM decrypt failed: %s\n", gpg_strerror (err)); + goto leave; + } + + put_membuf_printf (outbuf, + "(5:value%u:", (unsigned int)sessionkey_len); + put_membuf (outbuf, sessionkey, sessionkey_len); + put_membuf (outbuf, ")", 2); + + leave: + wipememory (ecc_ss, sizeof ecc_ss); + wipememory (mlkem_ss, sizeof mlkem_ss); + wipememory (kek, sizeof kek); + wipememory (sessionkey, sizeof sessionkey); + + mpi_release (ecc_ct_mpi); + mpi_release (mlkem_sk_mpi); + mpi_release (mlkem_ct_mpi); + mpi_release (encrypted_sessionkey_mpi); + gcry_free (fixed_info.data); + gcry_sexp_release (s_skey0); + gcry_sexp_release (s_skey1); + xfree (shadow_info0); + xfree (shadow_info1); + return err; +} + +/* DECRYPT the encrypted stuff (like encrypted session key) in + CIPHERTEXT using KEM API, with KEMID. Keys (or a key) are + specified in CTRL. DESC_TEXT is used to retrieve private key. + OPTION can be specified for upper layer option for KEM. Decrypted + stuff (like session key) is written to OUTBUF. + */ +gpg_error_t +agent_kem_decrypt (ctrl_t ctrl, const char *desc_text, int kemid, + const unsigned char *ciphertext, size_t ciphertextlen, + const unsigned char *option, size_t optionlen, + membuf_t *outbuf) +{ + gcry_sexp_t s_cipher = NULL; + gpg_error_t err = 0; + + /* For now, only PQC-PGP is supported. */ + if (kemid != KEM_PQC_PGP) + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + (void)optionlen; + if (kemid == KEM_PQC_PGP && option) + { + log_error ("PQC-PGP requires no option\n"); + return gpg_error (GPG_ERR_INV_ARG); + } + + if (!ctrl->have_keygrip) + { + log_error ("speculative decryption not yet supported\n"); + return gpg_error (GPG_ERR_NO_SECKEY); + } + + if (!ctrl->have_keygrip1) + { + log_error ("Composite KEM requires two KEYGRIPs\n"); + return gpg_error (GPG_ERR_NO_SECKEY); + } + + err = gcry_sexp_sscan (&s_cipher, NULL, (char*)ciphertext, ciphertextlen); + if (err) + { + log_error ("failed to convert ciphertext: %s\n", gpg_strerror (err)); + return gpg_error (GPG_ERR_INV_DATA); + } + + if (DBG_CRYPTO) + { + log_printhex (ctrl->keygrip, 20, "keygrip0:"); + log_printhex (ctrl->keygrip1, 20, "keygrip1:"); + gcry_log_debugsxp ("cipher", s_cipher); + } + + err = composite_pgp_kem_decrypt (ctrl, desc_text, s_cipher, outbuf); + + gcry_sexp_release (s_cipher); + return err; +} diff --git a/agent/trustlist.c b/agent/trustlist.c index 330f233b8..fce23de15 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -38,14 +38,14 @@ struct trustitem_s { struct { - int disabled:1; /* This entry is disabled. */ - int for_pgp:1; /* Set by '*' or 'P' as first flag. */ - int for_smime:1; /* Set by '*' or 'S' as first flag. */ - int relax:1; /* Relax checking of root certificate + unsigned int disabled:1; /* This entry is disabled. */ + unsigned int for_pgp:1; /* Set by '*' or 'P' as first flag. */ + unsigned int for_smime:1; /* Set by '*' or 'S' as first flag. */ + unsigned int relax:1; /* Relax checking of root certificate constraints. */ - int cm:1; /* Use chain model for validation. */ - int qual:1; /* Root CA for qualified signatures. */ - int de_vs:1; /* Root CA for de-vs compliant PKI. */ + unsigned int cm:1; /* Use chain model for validation. */ + unsigned int qual:1; /* Root CA for qualified signatures. */ + unsigned int de_vs:1; /* Root CA for de-vs compliant PKI. */ } flags; unsigned char fpr[20]; /* The binary fingerprint. */ }; diff --git a/build-aux/speedo.mk b/build-aux/speedo.mk index 8946c764c..e92a94231 100644 --- a/build-aux/speedo.mk +++ b/build-aux/speedo.mk @@ -43,58 +43,7 @@ # # The information required to sign the tarballs and binaries # are expected in the developer specific file ~/.gnupg-autogen.rc". -# Here is an example: -#--8<---------------cut here---------------start------------->8--- -# # Location of the released tarball archives. Note that this is an -# # internal archive and before uploading this to the public server, -# # manual tests should be run and the git release tagged and pushed. -# # This is greped by the Makefile. -# RELEASE_ARCHIVE=foo@somehost:tarball-archive -# -# # The key used to sign the GnuPG sources. -# # This is greped by the Makefile. -# RELEASE_SIGNKEY=6DAA6E64A76D2840571B4902528897B826403ADA -# -# # The key used to sign the VERSION files of some MSI installers. -# VERSION_SIGNKEY=02F38DFF731FF97CB039A1DA549E695E905BA208 -# -# # For signing Windows binaries we need to employ a Windows machine. -# # We connect to this machine via ssh and take the connection -# # parameters via .ssh/config. For example a VM could be specified -# # like this: -# # -# # Host authenticode-signhost -# # HostName localhost -# # Port 27042 -# # User gpgsign -# # -# # Depending on the used token it might be necessary to allow single -# # signon and unlock the token before running the make. The following -# # variable references this entry. This is greped by the Makefile. -# AUTHENTICODE_SIGNHOST=authenticode-signhost -# -# # The name of the signtool as used on Windows. -# # This is greped by the Makefile. -# AUTHENTICODE_TOOL="C:\Program Files (x86)\Windows Kits\10\bin\signtool.exe" -# -# # The URL for the timestamping service -# AUTHENTICODE_TSURL=http://rfc3161timestamp.globalsign.com/advanced -# -# # To use osslsigncode the follwing entries are required and -# # an empty string must be given for AUTHENTICODE_SIGNHOST. -# # They are greped by the Makefile. -# AUTHENTICODE_KEY=/home/foo/.gnupg/my-authenticode-key.p12 -# AUTHENTICODE_CERTS=/home/foo/.gnupg/my-authenticode-certs.pem -# -# # If a smartcard is used for the Authenticode signature these -# # entries are required instead: -# AUTHENTICODE_KEY=card -# AUTHENTICODE_CERTS=/home/foo/.gnupg/my_authenticode_cert.pem -# OSSLSIGNCODE=/usr/bin/osslsigncode -# OSSLPKCS11ENGINE=/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so -# SCUTEMODULE=/usr/local/lib/scute.so -# -#--8<---------------cut here---------------end--------------->8--- +# Use "gpg-authcode-sign.sh --template" to create a template. # We need to know our own name. @@ -124,6 +73,7 @@ help: @echo 'Use WIXPREFIX to provide the WIX binaries for the MSI package.' @echo ' Using WIX also requires wine with installed wine mono.' @echo ' See help-wixlib for more information' + @echo 'Set W32VERSION=w64 to build a 64 bit Windows version.' help-wixlib: @echo 'The buildsystem can create a wixlib to build MSI packages.' @@ -208,9 +158,12 @@ w32-release-offline: check-tools # to "this" from the unpacked sources. WHAT=git -# Set target to "native" or "w32" +# Set target to "native" or "w32". TARGETOS= +# To build a 64 bit Windows version also change this to "w64" +W32VERSION=w32 + # Set to 1 to use a pre-installed swdb.lst instead of the online version. CUSTOM_SWDB=0 @@ -231,6 +184,8 @@ TARBALLS=$(shell pwd)/../tarballs MAKE_J=6 # Name to use for the w32 installer and sources + + INST_NAME=gnupg-w32 # Use this to override the installaion directory for native builds. @@ -246,14 +201,6 @@ PATCHELF := $(shell patchelf --version 2>/dev/null >/dev/null || echo "echo plea define READ_AUTOGEN_template $(1) = $$(shell grep '^[[:blank:]]*$(1)[[:blank:]]*=' $$$$HOME/.gnupg-autogen.rc|cut -d= -f2|xargs) endef -$(eval $(call READ_AUTOGEN_template,AUTHENTICODE_SIGNHOST)) -$(eval $(call READ_AUTOGEN_template,AUTHENTICODE_TOOL)) -$(eval $(call READ_AUTOGEN_template,AUTHENTICODE_TSURL)) -$(eval $(call READ_AUTOGEN_template,AUTHENTICODE_KEY)) -$(eval $(call READ_AUTOGEN_template,AUTHENTICODE_CERTS)) -$(eval $(call READ_AUTOGEN_template,OSSLSIGNCODE)) -$(eval $(call READ_AUTOGEN_template,OSSLPKCS11ENGINE)) -$(eval $(call READ_AUTOGEN_template,SCUTEMODULE)) $(eval $(call READ_AUTOGEN_template,OVERRIDE_TARBALLS)) @@ -330,7 +277,12 @@ endif # Packages which are additionally build for 64 bit Windows. They are # only used for gpgex and thus we need to build them only if we want # a full installer. -speedo_w64_spkgs = +ifeq ($(W32VERSION),w64) + # Keep this empty + speedo_w64_spkgs = +else + speedo_w64_spkgs = +endif # Packages which use the gnupg autogen.sh build style speedo_gnupg_style = \ @@ -362,7 +314,7 @@ endif # Version numbers of the released packages gnupg_ver_this = $(shell cat $(topsrc)/VERSION) -gnupg_ver := $(shell awk '$$1=="gnupg24_ver" {print $$2}' swdb.lst) +gnupg_ver := $(shell awk '$$1=="gnupg26_ver" {print $$2}' swdb.lst) libgpg_error_ver := $(shell awk '$$1=="libgpg_error_ver" {print $$2}' swdb.lst) libgpg_error_sha1:= $(shell awk '$$1=="libgpg_error_sha1" {print $$2}' swdb.lst) @@ -409,7 +361,7 @@ sqlite_sha1 := $(shell awk '$$1=="sqlite_sha1_gz" {print $$2}' swdb.lst) sqlite_sha2 := $(shell awk '$$1=="sqlite_sha2_gz" {print $$2}' swdb.lst) -$(info Information from the version database) +$(info Information from the version database:) $(info GnuPG ..........: $(gnupg_ver) (building $(gnupg_ver_this))) $(info GpgRT ..........: $(libgpg_error_ver)) $(info Npth ...........: $(npth_ver)) @@ -424,6 +376,21 @@ $(info GPGME ..........: $(gpgme_ver)) $(info Pinentry .......: $(pinentry_ver)) endif +$(info Information for this run:) +$(info Build type .....: $(WHAT)) +$(info Target .........: $(TARGETOS)) +ifeq ($(TARGETOS),w32) +ifeq ($(W32VERSION),w64) + $(info Windows version : 64 bit) +else + $(info Windows version : 32 bit) +ifneq ($(W32VERSION),w32) + $(error W32VERSION is not set to a proper value: Use only w32 or w64) +endif +endif +endif + + # Version number for external packages pkg_config_ver = 0.23 libiconv_ver = 1.14 @@ -514,8 +481,8 @@ speedo_pkg_gettext_tar = $(pkg2rep)/gettext-$(gettext_ver).tar.gz speedo_pkg_npth_configure = --enable-static -speedo_pkg_libgpg_error_configure = --enable-static --enable-install-gpg-error-config -speedo_pkg_w64_libgpg_error_configure = --enable-static --enable-install-gpg-error-config +speedo_pkg_libgpg_error_configure = --enable-static +speedo_pkg_w64_libgpg_error_configure = --enable-static speedo_pkg_libassuan_configure = --enable-static speedo_pkg_w64_libassuan_configure = --enable-static @@ -651,12 +618,21 @@ report: report-speedo clean: clean-speedo + +ifeq ($(W32VERSION),w64) +W32CC_PREFIX = x86_64 +else +W32CC_PREFIX = i686 +endif + ifeq ($(TARGETOS),w32) -STRIP = i686-w64-mingw32-strip +STRIP = $(W32CC_PREFIX)-w64-mingw32-strip +W32STRIP32 = i686-w64-mingw32-strip else STRIP = strip endif -W32CC = i686-w64-mingw32-gcc +W32CC = $(W32CC_PREFIX)-w64-mingw32-gcc +W32CC32 = i686-w64-mingw32-gcc -include config.mk @@ -698,9 +674,9 @@ ifneq ($(TARGETOS),) # Determine build and host system build := $(shell $(topsrc)/autogen.sh --silent --print-build) ifeq ($(TARGETOS),w32) - speedo_autogen_buildopt := --build-w32 + speedo_autogen_buildopt := --build-$(W32VERSION) speedo_autogen_buildopt6 := --build-w64 - host := $(shell $(topsrc)/autogen.sh --silent --print-host --build-w32) + host := $(shell $(topsrc)/autogen.sh --silent --print-host --build-$(W32VERSION)) host6:= $(shell $(topsrc)/autogen.sh --silent --print-host --build-w64) speedo_host_build_option := --host=$(host) --build=$(build) speedo_host_build_option6 := --host=$(host6) --build=$(build) @@ -924,7 +900,7 @@ else ifneq ($(findstring $(1),$(speedo_gnupg_style)),) mkdir "$$$${pkgbdir}"; \ cd "$$$${pkgbdir}"; \ if [ -n "$(speedo_autogen_buildopt)" ]; then \ - eval AUTOGEN_SH_SILENT=1 w32root="$(idir)" \ + eval AUTOGEN_SH_SILENT=1 $(W32VERSION)root="$(idir)" \ "$$$${pkgsdir}/autogen.sh" \ $(speedo_autogen_buildopt) \ $$$${pkgcfg} $$$${pkgextracflags}; \ @@ -1133,8 +1109,8 @@ ifneq ($(TARGETOS),w32) echo "speedo: * Now copy $(idir)/ to the final location and" ;\ echo "speedo: * adjust $(idir)/bin/gpgconf.ctl accordingly" ;\ echo "speedo: * Or run:" ;\ - echo "speedo: * make -f $(topsrc)/build-aux/speedo.mk install SYSROOT=/usr/local/gnupg24" ;\ - echo "speedo: * ldconfig -n /usr/local/gnupg24/lib";\ + echo "speedo: * make -f $(topsrc)/build-aux/speedo.mk install SYSROOT=/usr/local/gnupg26" ;\ + echo "speedo: * ldconfig -n /usr/local/gnupg26/lib";\ echo "speedo: */") endif @@ -1149,8 +1125,8 @@ ifneq ($(TARGETOS),w32) echo "speedo: ERROR: SYSROOT has not been given";\ echo "speedo: Set SYSROOT to the desired install directory";\ echo "speedo: Example:";\ - echo "speedo: make -f $(topsrc)/build-aux/speedo.mk install SYSROOT=/usr/local/gnupg24";\ - echo "speedo: ldconfig -n /usr/local/gnupg24/lib";\ + echo "speedo: make -f $(topsrc)/build-aux/speedo.mk install SYSROOT=/usr/local/gnupg26";\ + echo "speedo: ldconfig -n /usr/local/gnupg26/lib";\ exit 1;\ fi;\ if [ ! -d "$$SYSROOT"/bin ]; then if ! mkdir "$$SYSROOT"/bin; then \ @@ -1238,13 +1214,13 @@ $(bdir)/README.txt: $(bdir)/NEWS.tmp $(topsrc)/README $(w32src)/README.txt \ $(bdir)/g4wihelp.dll: $(w32src)/g4wihelp.c $(w32src)/exdll.h $(w32src)/exdll.c (set -e; cd $(bdir); \ - $(W32CC) -DUNICODE -static-libgcc -I . -O2 -c \ + $(W32CC32) -DUNICODE -static-libgcc -I . -O2 -c \ -o exdll.o $(w32src)/exdll.c; \ - $(W32CC) -DUNICODE -static-libgcc -I. -shared -O2 \ + $(W32CC32) -DUNICODE -static-libgcc -I. -shared -O2 \ -o g4wihelp.dll $(w32src)/g4wihelp.c exdll.o \ -lwinmm -lgdi32 -luserenv \ -lshell32 -loleaut32 -lshlwapi -lmsimg32; \ - $(STRIP) g4wihelp.dll) + $(W32STRIP32) g4wihelp.dll) w32_insthelpers: $(bdir)/g4wihelp.dll @@ -1340,7 +1316,7 @@ wixlib: installer $(bdir)/README.txt $(w32src)/wixlib.wxs ) define MKSWDB_commands - ( pref="#+macro: gnupg24_w32_$(3)" ;\ + ( pref="#+macro: gnupg26_w32_$(3)" ;\ echo "$${pref}ver $(INST_VERSION)_$(BUILD_DATESTR)" ;\ echo "$${pref}date $(2)" ;\ echo "$${pref}size $$(wc -c <$(1)|awk '{print int($$1/1024)}')k";\ @@ -1351,35 +1327,13 @@ endef # Sign the file $1 and save the result as $2 define AUTHENTICODE_sign - set -e;\ - if [ -n "$(AUTHENTICODE_SIGNHOST)" ]; then \ - echo "speedo: Signing via host $(AUTHENTICODE_SIGNHOST)";\ - scp $(1) "$(AUTHENTICODE_SIGNHOST):a.exe" ;\ - ssh "$(AUTHENTICODE_SIGNHOST)" '$(AUTHENTICODE_TOOL)' sign \ - /a /n '"g10 Code GmbH"' \ - /tr '$(AUTHENTICODE_TSURL)' /td sha256 \ - /fd sha256 /du https://gnupg.org a.exe ;\ - scp "$(AUTHENTICODE_SIGNHOST):a.exe" $(2);\ - echo "speedo: signed file is '$(2)'" ;\ - elif [ "$(AUTHENTICODE_KEY)" = card ]; then \ - echo "speedo: Signing using a card: '$(1)'";\ - $(OSSLSIGNCODE) sign \ - -pkcs11engine $(OSSLPKCS11ENGINE) \ - -pkcs11module $(SCUTEMODULE) \ - -certs $(AUTHENTICODE_CERTS) \ - -h sha256 -n GnuPG -i https://gnupg.org \ - -ts $(AUTHENTICODE_TSURL) \ - -in $(1) -out $(2).tmp ; mv $(2).tmp $(2) ; \ - elif [ -e "$(AUTHENTICODE_KEY)" ]; then \ - echo "speedo: Signing using key $(AUTHENTICODE_KEY)";\ - osslsigncode sign -certs $(AUTHENTICODE_CERTS) \ - -pkcs12 $(AUTHENTICODE_KEY) -askpass \ - -ts "$(AUTHENTICODE_TSURL)" \ - -h sha256 -n GnuPG -i https://gnupg.org \ - -in $(1) -out $(2) ;\ + (set -e; \ + if gpg-authcode-sign.sh --version >/dev/null; then \ + gpg-authcode-sign.sh "$(1)" "$(2)"; \ else \ - echo "speedo: WARNING: Binaries are not signed"; \ - fi + echo 2>&1 "warning: Please install gpg-authcode-sign.sh to sign files." ;\ + [ "$(1)" != "$(2)" ] && cp "$(1)" "$(2)" ;\ + fi) endef # Help target for testing to sign a file. diff --git a/build-aux/speedo/w32/inst.nsi b/build-aux/speedo/w32/inst.nsi index 6c4b84969..acd32ad27 100644 --- a/build-aux/speedo/w32/inst.nsi +++ b/build-aux/speedo/w32/inst.nsi @@ -46,7 +46,7 @@ Unicode true !define PRETTY_PACKAGE "GNU Privacy Guard" !define PRETTY_PACKAGE_SHORT "GnuPG" !define COMPANY "The GnuPG Project" -!define COPYRIGHT "Copyright (C) 2021 g10 Code GmbH" +!define COPYRIGHT "Copyright (C) 2024 g10 Code GmbH" !define DESCRIPTION "GnuPG: The GNU Privacy Guard for Windows" !define INSTALL_DIR "GnuPG" @@ -63,13 +63,13 @@ Unicode true GnuPG includes an advanced key management facility and is compliant \ with the OpenPGP Internet standard as described in RFC-4880. \ \r\n\r\n$_CLICK \ - \r\n\r\n\r\n\r\n\r\nThis is GnuPG version ${VERSION}.\r\n\ + \r\n\r\n\r\n\r\n\r\nThis is GnuPG version ${VERSION} (64 bit).\r\n\ File version: ${PROD_VERSION}\r\n\ Release date: ${BUILD_ISODATE}" !define ABOUT_GERMAN \ "GnuPG is die häufigst verwendete Software zur Mail- und Datenverschlüsselung.\ \r\n\r\n$_CLICK \ - \r\n\r\n\r\n\r\n\r\nDies ist GnuPG Version ${VERSION}.\r\n\ + \r\n\r\n\r\n\r\n\r\nDies ist GnuPG Version ${VERSION} (64 bit).\r\n\ Dateiversion: ${PROD_VERSION}\r\n\ Releasedatum: ${BUILD_ISODATE}" @@ -119,7 +119,7 @@ OutFile "${NAME}-${VERSION}_${BUILD_DATESTR}.exe" !ifndef INSTALL_DIR !define INSTALL_DIR "GnuPG" !endif -InstallDir "$PROGRAMFILES\${INSTALL_DIR}" +InstallDir "$PROGRAMFILES64\${INSTALL_DIR}" # Add version information to the file properties. VIProductVersion "${PROD_VERSION}" @@ -1465,7 +1465,12 @@ Function .onInit Call G4wRunOnce - SetOutPath $TEMP + ${IfNot} ${RunningX64} + MessageBox MB_OK "Sorry this version runs only on x64 machines" + Abort + ${EndIf} + + SetOutPath $TEMP #!ifdef SOURCES # File /oname=gpgspltmp.bmp "${TOP_SRCDIR}/doc/logo/gnupg-logo-400px.bmp" # # We play the tune only for the soruce installer @@ -1486,7 +1491,7 @@ Function .onInit Var /GLOBAL changed_dir # Check if the install directory was modified on the command line - StrCmp "$INSTDIR" "$PROGRAMFILES\${INSTALL_DIR}" unmodified 0 + StrCmp "$INSTDIR" "$PROGRAMFILES64\${INSTALL_DIR}" unmodified 0 # It is modified. Save that value. StrCpy $changed_dir "$INSTDIR" diff --git a/common/Makefile.am b/common/Makefile.am index 91718f99e..a4840fbdd 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -65,7 +65,7 @@ common_sources = \ homedir.c \ gettime.c gettime.h \ yesno.c \ - b64enc.c b64dec.c zb32.c zb32.h \ + zb32.c zb32.h \ convert.c \ percent.c \ mbox-util.c mbox-util.h \ @@ -97,8 +97,8 @@ common_sources = \ openpgp-fpr.c \ comopt.c comopt.h \ compliance.c compliance.h \ - pkscreening.c pkscreening.h - + pkscreening.c pkscreening.h \ + kem.c if HAVE_W32_SYSTEM common_sources += w32-reg.c w32-cmdline.c @@ -161,11 +161,12 @@ module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ - t-name-value t-ccparray t-recsel t-w32-cmdline t-b64 + t-name-value t-ccparray t-recsel t-w32-cmdline t-exechelp + if HAVE_W32_SYSTEM module_tests += t-w32-reg else -module_tests += t-exechelp t-exectool +module_tests += t-exectool endif if MAINTAINER_MODE @@ -196,7 +197,6 @@ t_gettime_LDADD = $(t_common_ldadd) t_sysutils_LDADD = $(t_common_ldadd) t_helpfile_LDADD = $(t_common_ldadd) t_sexputil_LDADD = $(t_common_ldadd) -t_b64_LDADD = $(t_common_ldadd) t_exechelp_LDADD = $(t_common_ldadd) t_exectool_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) diff --git a/common/asshelp.c b/common/asshelp.c index eb3e41bf5..5a40e0380 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -386,7 +386,8 @@ start_new_service (assuan_context_t *r_ctx, const char *opt_lc_ctype, const char *opt_lc_messages, session_env_t session_env, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { @@ -445,7 +446,7 @@ start_new_service (assuan_context_t *r_ctx, } err = assuan_socket_connect (ctx, sockname, 0, connect_flags); - if (err && autostart) + if (err && (flags & ASSHELP_FLAG_AUTOSTART)) { char *abs_homedir; lock_spawn_t lock; @@ -523,16 +524,12 @@ start_new_service (assuan_context_t *r_ctx, && assuan_socket_connect (ctx, sockname, 0, connect_flags)) { #ifdef HAVE_W32_SYSTEM - err = gnupg_spawn_process_detached (program? program : program_name, - argv, NULL); + err = gnupg_process_spawn (program? program : program_name, argv, + GNUPG_PROCESS_DETACHED, + NULL, NULL, NULL); #else /*!W32*/ - pid_t pid; - - err = gnupg_spawn_process_fd (program? program : program_name, - argv, -1, -1, -1, &pid); - if (!err) - err = gnupg_wait_process (program? program : program_name, - pid, 1, NULL); + err = gnupg_process_spawn (program? program : program_name, argv, + 0, NULL, NULL, NULL); #endif /*!W32*/ if (err) log_error ("failed to start %s '%s': %s\n", @@ -551,7 +548,8 @@ start_new_service (assuan_context_t *r_ctx, xfree (sockname); if (err) { - if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) + if ((flags & ASSHELP_FLAG_AUTOSTART) + || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) log_error ("can't connect to the %s: %s\n", printed_name, gpg_strerror (err)); assuan_release (ctx); @@ -603,55 +601,58 @@ start_new_gpg_agent (assuan_context_t *r_ctx, const char *opt_lc_ctype, const char *opt_lc_messages, session_env_t session_env, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { return start_new_service (r_ctx, GNUPG_MODULE_NAME_AGENT, errsource, agent_program, opt_lc_ctype, opt_lc_messages, session_env, - autostart, verbose, debug, + flags, verbose, debug, status_cb, status_cb_arg); } /* Try to connect to the dirmngr via a socket. On platforms - supporting it, start it up if needed and if AUTOSTART is true. + supporting it, start it up if needed and if ASSHELP_FLAG_AUTOSTART is set. Returns a new assuan context at R_CTX or an error code. */ gpg_error_t start_new_keyboxd (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *keyboxd_program, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { return start_new_service (r_ctx, GNUPG_MODULE_NAME_KEYBOXD, errsource, keyboxd_program, NULL, NULL, NULL, - autostart, verbose, debug, + flags, verbose, debug, status_cb, status_cb_arg); } /* Try to connect to the dirmngr via a socket. On platforms - supporting it, start it up if needed and if AUTOSTART is true. + supporting it, start it up if needed and if ASSHELP_FLAG_AUTOSTART is set. Returns a new assuan context at R_CTX or an error code. */ gpg_error_t start_new_dirmngr (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *dirmngr_program, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg) { #ifndef USE_DIRMNGR_AUTO_START - autostart = 0; + flags &= ~ASSHELP_FLAG_AUTOSTART; /* Clear flag. */ #endif return start_new_service (r_ctx, GNUPG_MODULE_NAME_DIRMNGR, errsource, dirmngr_program, NULL, NULL, NULL, - autostart, verbose, debug, + flags, verbose, debug, status_cb, status_cb_arg); } diff --git a/common/asshelp.h b/common/asshelp.h index e7e43bd1b..bca50759d 100644 --- a/common/asshelp.h +++ b/common/asshelp.h @@ -37,6 +37,8 @@ #include "util.h" /*-- asshelp.c --*/ +#define ASSHELP_FLAG_AUTOSTART 1 /* Autostart the new service. */ + void setup_libassuan_logging (unsigned int *debug_var_address, int (*log_monitor)(assuan_context_t ctx, @@ -61,7 +63,8 @@ start_new_gpg_agent (assuan_context_t *r_ctx, const char *opt_lc_ctype, const char *opt_lc_messages, session_env_t session_env, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg); @@ -71,7 +74,8 @@ gpg_error_t start_new_keyboxd (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *keyboxd_program, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg); @@ -81,7 +85,8 @@ gpg_error_t start_new_dirmngr (assuan_context_t *r_ctx, gpg_err_source_t errsource, const char *dirmngr_program, - int autostart, int verbose, int debug, + unsigned int flags, + int verbose, int debug, gpg_error_t (*status_cb)(ctrl_t, int, ...), ctrl_t status_cb_arg); diff --git a/common/audit.c b/common/audit.c index ae0d45216..42a2cf6d6 100644 --- a/common/audit.c +++ b/common/audit.c @@ -45,8 +45,8 @@ struct log_item_s int intvalue; /* A logged integer value. */ char *string; /* A malloced string or NULL. */ ksba_cert_t cert; /* A certifciate or NULL. */ - int have_err:1; - int have_intvalue:1; + unsigned int have_err:1; + unsigned int have_intvalue:1; }; typedef struct log_item_s *log_item_t; diff --git a/common/b64dec.c b/common/b64dec.c deleted file mode 100644 index 2904b0471..000000000 --- a/common/b64dec.c +++ /dev/null @@ -1,299 +0,0 @@ -/* b64dec.c - Simple Base64 decoder. - * Copyright (C) 2008, 2011 Free Software Foundation, Inc. - * Copyright (C) 2008, 2011, 2016 g10 Code GmbH - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> - -#include "i18n.h" -#include "util.h" - - -/* The reverse base-64 list used for base-64 decoding. */ -static unsigned char const asctobin[128] = - { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff - }; - -enum decoder_states - { - s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin, - s_b64_0, s_b64_1, s_b64_2, s_b64_3, - s_waitendtitle, s_waitend - }; - - - -/* Initialize the context for the base64 decoder. If TITLE is NULL a - plain base64 decoding is done. If it is the empty string the - decoder will skip everything until a "-----BEGIN " line has been - seen, decoding ends at a "----END " line. */ -gpg_error_t -b64dec_start (struct b64state *state, const char *title) -{ - memset (state, 0, sizeof *state); - if (title) - { - state->title = xtrystrdup (title); - if (!state->title) - state->lasterr = gpg_error_from_syserror (); - else - state->idx = s_init; - } - else - state->idx = s_b64_0; - return state->lasterr; -} - - -/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the - new length of the buffer at R_NBYTES. */ -gpg_error_t -b64dec_proc (struct b64state *state, void *buffer, size_t length, - size_t *r_nbytes) -{ - enum decoder_states ds = state->idx; - unsigned char val = state->radbuf[0]; - int pos = state->quad_count; - char *d, *s; - - if (state->lasterr) - return state->lasterr; - - if (state->stop_seen) - { - *r_nbytes = 0; - state->lasterr = gpg_error (GPG_ERR_EOF); - xfree (state->title); - state->title = NULL; - return state->lasterr; - } - - for (s=d=buffer; length && !state->stop_seen; length--, s++) - { - again: - switch (ds) - { - case s_idle: - if (*s == '\n') - { - ds = s_lfseen; - pos = 0; - } - break; - case s_init: - ds = s_lfseen; - /* fall through */ - case s_lfseen: - if (*s != "-----BEGIN "[pos]) - { - ds = s_idle; - goto again; - } - else if (pos == 10) - { - pos = 0; - ds = s_beginseen; - } - else - pos++; - break; - case s_beginseen: - if (*s != "PGP "[pos]) - ds = s_begin; /* Not a PGP armor. */ - else if (pos == 3) - ds = s_waitheader; - else - pos++; - break; - case s_waitheader: - if (*s == '\n') - ds = s_waitblank; - break; - case s_waitblank: - if (*s == '\n') - ds = s_b64_0; /* blank line found. */ - else if (*s == ' ' || *s == '\r' || *s == '\t') - ; /* Ignore spaces. */ - else - { - /* Armor header line. Note that we don't care that our - * FSM accepts a header prefixed with spaces. */ - ds = s_waitheader; /* Wait for next header. */ - } - break; - case s_begin: - if (*s == '\n') - ds = s_b64_0; - break; - case s_b64_0: - case s_b64_1: - case s_b64_2: - case s_b64_3: - { - int c; - - if (*s == '-' && state->title) - { - /* Not a valid Base64 character: assume end - header. */ - ds = s_waitend; - } - else if (*s == '=') - { - /* Pad character: stop */ - if (ds == s_b64_1) - *d++ = val; - ds = state->title? s_waitendtitle : s_waitend; - } - else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t') - ; /* Skip white spaces. */ - else if ( (*s & 0x80) - || (c = asctobin[*(unsigned char *)s]) == 255) - { - /* Skip invalid encodings. */ - state->invalid_encoding = 1; - } - else if (ds == s_b64_0) - { - val = c << 2; - ds = s_b64_1; - } - else if (ds == s_b64_1) - { - val |= (c>>4)&3; - *d++ = val; - val = (c<<4)&0xf0; - ds = s_b64_2; - } - else if (ds == s_b64_2) - { - val |= (c>>2)&15; - *d++ = val; - val = (c<<6)&0xc0; - ds = s_b64_3; - } - else - { - val |= c&0x3f; - *d++ = val; - ds = s_b64_0; - } - } - break; - case s_waitendtitle: - if (*s == '-') - ds = s_waitend; - break; - case s_waitend: - if ( *s == '\n') - state->stop_seen = 1; - break; - default: - BUG(); - } - } - - - state->idx = ds; - state->radbuf[0] = val; - state->quad_count = pos; - *r_nbytes = (d -(char*) buffer); - return 0; -} - - -/* This function needs to be called before releasing the decoder - state. It may return an error code in case an encoding error has - been found during decoding. */ -gpg_error_t -b64dec_finish (struct b64state *state) -{ - xfree (state->title); - state->title = NULL; - - if (state->lasterr) - return state->lasterr; - - return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; -} - - -/* Convert STRING consisting of base64 characters into its binary - * representation and store the result in a newly allocated buffer at - * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain - * base64 decoding is done. If it is the empty string the decoder - * will skip everything until a "-----BEGIN " line has been seen, - * decoding then ends at a "----END " line. On failure the function - * returns an error code and sets R_BUFFER to NULL. If the decoded - * data has a length of 0 a dummy buffer will still be allocated and - * the length is set to 0. */ -gpg_error_t -b64decode (const char *string, const char *title, - void **r_buffer, size_t *r_buflen) -{ - gpg_error_t err; - struct b64state state; - size_t nbytes; - char *buffer; - - *r_buffer = NULL; - *r_buflen = 0; - - buffer = xtrystrdup (string); - if (!buffer) - return gpg_error_from_syserror(); - - err = b64dec_start (&state, title); - if (err) - { - xfree (buffer); - return err; - } - b64dec_proc (&state, buffer, strlen (buffer), &nbytes); - err = b64dec_finish (&state); - if (err) - xfree (buffer); - else - { - *r_buffer = buffer; - *r_buflen = nbytes; - } - return err; -} diff --git a/common/b64enc.c b/common/b64enc.c deleted file mode 100644 index 7846dcb3e..000000000 --- a/common/b64enc.c +++ /dev/null @@ -1,423 +0,0 @@ -/* b64enc.c - Simple Base64 encoder. - * Copyright (C) 2001, 2003, 2004, 2008, 2010, - * 2011 Free Software Foundation, Inc. - * Copyright (C) 2001, 2003, 2004, 2008, 2010, - * 2011 g10 Code GmbH - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> - -#include "i18n.h" -#include "util.h" - -#define B64ENC_DID_HEADER 1 -#define B64ENC_DID_TRAILER 2 -#define B64ENC_NO_LINEFEEDS 16 -#define B64ENC_USE_PGPCRC 32 - -/* The base-64 character list */ -static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -/* Stuff required to create the OpenPGP CRC. This crc_table has been - created using this code: - - #include <stdio.h> - #include <stdint.h> - - #define CRCPOLY 0x864CFB - - int - main (void) - { - int i, j; - uint32_t t; - uint32_t crc_table[256]; - - crc_table[0] = 0; - for (i=j=0; j < 128; j++ ) - { - t = crc_table[j]; - if ( (t & 0x00800000) ) - { - t <<= 1; - crc_table[i++] = t ^ CRCPOLY; - crc_table[i++] = t; - } - else - { - t <<= 1; - crc_table[i++] = t; - crc_table[i++] = t ^ CRCPOLY; - } - } - - puts ("static const u32 crc_table[256] = {"); - for (i=j=0; i < 256; i++) - { - printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]); - if (i != 255) - { - putchar (','); - if ( ++j > 5) - { - j = 0; - putchar ('\n'); - } - } - } - puts ("\n};"); - return 0; - } -*/ -#define CRCINIT 0xB704CE -static const u32 crc_table[256] = { - 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a, - 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf, - 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272, - 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e, - 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa, - 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f, - 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b, - 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7, - 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a, - 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af, - 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29, - 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5, - 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1, - 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad, - 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099, - 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375, - 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821, - 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4, - 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049, - 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5, - 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791, - 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52, - 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66, - 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a, - 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337, - 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2, - 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6, - 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a, - 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e, - 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132, - 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506, - 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea, - 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c, - 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9, - 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604, - 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8, - 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc, - 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69, - 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d, - 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1, - 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c, - 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9, - 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538 -}; - - -static gpg_error_t -enc_start (struct b64state *state, FILE *fp, estream_t stream, - const char *title) -{ - memset (state, 0, sizeof *state); - state->fp = fp; - state->stream = stream; - state->lasterr = 0; - if (title && !*title) - state->flags |= B64ENC_NO_LINEFEEDS; - else if (title) - { - if (!strncmp (title, "PGP ", 4)) - { - state->flags |= B64ENC_USE_PGPCRC; - state->crc = CRCINIT; - } - state->title = xtrystrdup (title); - if (!state->title) - state->lasterr = gpg_error_from_syserror (); - } - return state->lasterr; -} - - -/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL - and not an empty string, this string will be used as the title for - the armor lines, with TITLE being an empty string, we don't write - the header lines and furthermore even don't write any linefeeds. - If TITLE starts with "PGP " the OpenPGP CRC checksum will be - written as well. With TITLE being NULL, we merely don't write - header but make sure that lines are not too long. Note, that we - don't write any output unless at least one byte get written using - b64enc_write. */ -gpg_error_t -b64enc_start (struct b64state *state, FILE *fp, const char *title) -{ - return enc_start (state, fp, NULL, title); -} - -/* Same as b64enc_start but takes an estream. */ -gpg_error_t -b64enc_start_es (struct b64state *state, estream_t fp, const char *title) -{ - return enc_start (state, NULL, fp, title); -} - - -static int -my_fputs (const char *string, struct b64state *state) -{ - if (state->stream) - return es_fputs (string, state->stream); - else - return fputs (string, state->fp); -} - - -/* Write NBYTES from BUFFER to the Base 64 stream identified by - STATE. With BUFFER and NBYTES being 0, merely do a fflush on the - stream. */ -gpg_error_t -b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) -{ - unsigned char radbuf[4]; - int idx, quad_count; - const unsigned char *p; - - if (state->lasterr) - return state->lasterr; - - if (!nbytes) - { - if (buffer) - if (state->stream? es_fflush (state->stream) : fflush (state->fp)) - goto write_error; - return 0; - } - - if (!(state->flags & B64ENC_DID_HEADER)) - { - if (state->title) - { - if ( my_fputs ("-----BEGIN ", state) == EOF - || my_fputs (state->title, state) == EOF - || my_fputs ("-----\n", state) == EOF) - goto write_error; - if ( (state->flags & B64ENC_USE_PGPCRC) - && my_fputs ("\n", state) == EOF) - goto write_error; - } - - state->flags |= B64ENC_DID_HEADER; - } - - idx = state->idx; - quad_count = state->quad_count; - assert (idx < 4); - memcpy (radbuf, state->radbuf, idx); - - if ( (state->flags & B64ENC_USE_PGPCRC) ) - { - size_t n; - u32 crc = state->crc; - - for (p=buffer, n=nbytes; n; p++, n-- ) - crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p]; - state->crc = (crc & 0x00ffffff); - } - - for (p=buffer; nbytes; p++, nbytes--) - { - radbuf[idx++] = *p; - if (idx > 2) - { - char tmp[4]; - - tmp[0] = bintoasc[(*radbuf >> 2) & 077]; - tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; - tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; - tmp[3] = bintoasc[radbuf[2]&077]; - if (state->stream) - { - for (idx=0; idx < 4; idx++) - es_putc (tmp[idx], state->stream); - idx = 0; - if (es_ferror (state->stream)) - goto write_error; - } - else - { - for (idx=0; idx < 4; idx++) - putc (tmp[idx], state->fp); - idx = 0; - if (ferror (state->fp)) - goto write_error; - } - if (++quad_count >= (64/4)) - { - quad_count = 0; - if (!(state->flags & B64ENC_NO_LINEFEEDS) - && my_fputs ("\n", state) == EOF) - goto write_error; - } - } - } - memcpy (state->radbuf, radbuf, idx); - state->idx = idx; - state->quad_count = quad_count; - return 0; - - write_error: - state->lasterr = gpg_error_from_syserror (); - if (state->title) - { - xfree (state->title); - state->title = NULL; - } - return state->lasterr; -} - - -gpg_error_t -b64enc_finish (struct b64state *state) -{ - gpg_error_t err = 0; - unsigned char radbuf[4]; - int idx, quad_count; - char tmp[4]; - - if (state->lasterr) - return state->lasterr; - - if (!(state->flags & B64ENC_DID_HEADER)) - goto cleanup; - - /* Flush the base64 encoding */ - idx = state->idx; - quad_count = state->quad_count; - assert (idx < 4); - memcpy (radbuf, state->radbuf, idx); - - if (idx) - { - tmp[0] = bintoasc[(*radbuf>>2)&077]; - if (idx == 1) - { - tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077]; - tmp[2] = '='; - tmp[3] = '='; - } - else - { - tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; - tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077]; - tmp[3] = '='; - } - if (state->stream) - { - for (idx=0; idx < 4; idx++) - es_putc (tmp[idx], state->stream); - if (es_ferror (state->stream)) - goto write_error; - } - else - { - for (idx=0; idx < 4; idx++) - putc (tmp[idx], state->fp); - if (ferror (state->fp)) - goto write_error; - } - - if (++quad_count >= (64/4)) - { - quad_count = 0; - if (!(state->flags & B64ENC_NO_LINEFEEDS) - && my_fputs ("\n", state) == EOF) - goto write_error; - } - } - - /* Finish the last line and write the trailer. */ - if (quad_count - && !(state->flags & B64ENC_NO_LINEFEEDS) - && my_fputs ("\n", state) == EOF) - goto write_error; - - if ( (state->flags & B64ENC_USE_PGPCRC) ) - { - /* Write the CRC. */ - my_fputs ("=", state); - radbuf[0] = state->crc >>16; - radbuf[1] = state->crc >> 8; - radbuf[2] = state->crc; - tmp[0] = bintoasc[(*radbuf>>2)&077]; - tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; - tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; - tmp[3] = bintoasc[radbuf[2]&077]; - if (state->stream) - { - for (idx=0; idx < 4; idx++) - es_putc (tmp[idx], state->stream); - if (es_ferror (state->stream)) - goto write_error; - } - else - { - for (idx=0; idx < 4; idx++) - putc (tmp[idx], state->fp); - if (ferror (state->fp)) - goto write_error; - } - if (!(state->flags & B64ENC_NO_LINEFEEDS) - && my_fputs ("\n", state) == EOF) - goto write_error; - } - - if (state->title) - { - if ( my_fputs ("-----END ", state) == EOF - || my_fputs (state->title, state) == EOF - || my_fputs ("-----\n", state) == EOF) - goto write_error; - } - - goto cleanup; - - write_error: - err = gpg_error_from_syserror (); - - cleanup: - if (state->title) - { - xfree (state->title); - state->title = NULL; - } - state->fp = NULL; - state->stream = NULL; - state->lasterr = err; - return err; -} diff --git a/common/compliance.c b/common/compliance.c index 84449af25..2df10d2e2 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -139,7 +139,7 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { - enum { is_rsa, is_dsa, is_elg, is_ecc } algotype; + enum { is_rsa, is_dsa, is_elg, is_ecc, is_kem } algotype; int result = 0; if (! initialized) @@ -173,6 +173,10 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, case PUBKEY_ALGO_ELGAMAL: return 0; /* Signing with Elgamal is not at all supported. */ + case PUBKEY_ALGO_KYBER: + algotype = is_kem; + break; + default: /* Unknown. */ return 0; } @@ -227,6 +231,10 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, || !strcmp (curvename, "brainpoolP512r1"))); break; + case is_kem: + result = 0; + break; + default: result = 0; } diff --git a/common/dynload.h b/common/dynload.h index 6ac7b4e17..af3906c81 100644 --- a/common/dynload.h +++ b/common/dynload.h @@ -34,12 +34,15 @@ #ifndef __MINGW32__ # include <dlfcn.h> #else -# include <errhandlingapi.h> -# include <handleapi.h> -# include <libloaderapi.h> +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> # include "utf8conv.h" # include "mischelp.h" -# define RTLD_LAZY 0 +# ifndef RTLD_LAZY +# define RTLD_LAZY 0 +# endif static inline void * dlopen (const char *name, int flag) diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c index fa613449d..d90b4e8c7 100644 --- a/common/exechelp-posix.c +++ b/common/exechelp-posix.c @@ -84,12 +84,6 @@ my_error_from_syserror (void) return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); } -static inline gpg_error_t -my_error (int errcode) -{ - return gpg_err_make (default_errsource, errcode); -} - /* Return the maximum number of currently allowed open file descriptors. Only useful on POSIX systems but returns a value on @@ -273,73 +267,6 @@ get_all_open_fds (void) } -/* The exec core used right after the fork. This will never return. */ -static void -do_exec (const char *pgmname, const char *argv[], - int fd_in, int fd_out, int fd_err, - int *except, unsigned int flags) -{ - char **arg_list; - int i, j; - int fds[3]; - int nodevnull[3]; - - fds[0] = fd_in; - fds[1] = fd_out; - fds[2] = fd_err; - - nodevnull[0] = !!(flags & GNUPG_SPAWN_KEEP_STDIN); - nodevnull[1] = !!(flags & GNUPG_SPAWN_KEEP_STDOUT); - nodevnull[2] = !!(flags & GNUPG_SPAWN_KEEP_STDERR); - - /* Create the command line argument array. */ - i = 0; - if (argv) - while (argv[i]) - i++; - arg_list = xcalloc (i+2, sizeof *arg_list); - arg_list[0] = strrchr (pgmname, '/'); - if (arg_list[0]) - arg_list[0]++; - else - arg_list[0] = xstrdup (pgmname); - if (argv) - for (i=0,j=1; argv[i]; i++, j++) - arg_list[j] = (char*)argv[i]; - - /* Assign /dev/null to unused FDs. */ - for (i=0; i <= 2; i++) - { - if (nodevnull[i]) - continue; - if (fds[i] == -1) - { - fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY); - if (fds[i] == -1) - log_fatal ("failed to open '%s': %s\n", - "/dev/null", strerror (errno)); - } - } - - /* Connect the standard files. */ - for (i=0; i <= 2; i++) - { - if (nodevnull[i]) - continue; - if (fds[i] != i && dup2 (fds[i], i) == -1) - log_fatal ("dup2 std%s failed: %s\n", - i==0?"in":i==1?"out":"err", strerror (errno)); - } - - /* Close all other files. */ - close_all_fds (3, except); - - execv (pgmname, arg_list); - /* No way to print anything, as we have closed all streams. */ - _exit (127); -} - - static gpg_error_t do_create_pipe (int filedes[2]) { @@ -431,487 +358,669 @@ gnupg_close_pipe (int fd) close (fd); } +#include <sys/socket.h> -/* Fork and exec the PGMNAME, see exechelp.h for details. */ -gpg_error_t -gnupg_spawn_process (const char *pgmname, const char *argv[], - int *except, unsigned int flags, - estream_t *r_infp, - estream_t *r_outfp, - estream_t *r_errfp, - pid_t *pid) -{ - gpg_error_t err; - int inpipe[2] = {-1, -1}; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - estream_t infp = NULL; - estream_t outfp = NULL; - estream_t errfp = NULL; - int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); +struct gnupg_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; + pid_t pid; + int fd_in; + int fd_out; + int fd_err; + int wstatus; +}; - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *pid = (pid_t)(-1); /* Always required. */ +static int gnupg_process_syscall_func_initialized; - if (r_infp) - { - err = create_pipe_and_estream (inpipe, &infp, 1, nonblock); - if (err) - return err; - } +/* Functions called before and after blocking syscalls. */ +static void (*pre_syscall_func) (void); +static void (*post_syscall_func) (void); - if (r_outfp) +static void +check_syscall_func (void) +{ + if (!gnupg_process_syscall_func_initialized) { - err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock); - if (err) - { - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - return err; - } + gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func); + gnupg_process_syscall_func_initialized = 1; } +} - if (r_errfp) - { - err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock); - if (err) - { - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - return err; - } - } +static void +pre_syscall (void) +{ + if (pre_syscall_func) + pre_syscall_func (); +} +static void +post_syscall (void) +{ + if (post_syscall_func) + post_syscall_func (); +} - *pid = fork (); - if (*pid == (pid_t)(-1)) - { - err = my_error_from_syserror (); - log_error (_("error forking process: %s\n"), gpg_strerror (err)); - - if (infp) - es_fclose (infp); - else if (inpipe[1] != -1) - close (inpipe[1]); - if (inpipe[0] != -1) - close (inpipe[0]); - - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != -1) - close (outpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - - if (errfp) - es_fclose (errfp); - else if (errpipe[0] != -1) - close (errpipe[0]); - if (errpipe[1] != -1) - close (errpipe[1]); - return err; - } - if (!*pid) +static gpg_err_code_t +do_create_socketpair (int filedes[2]) +{ + gpg_error_t err = 0; + + pre_syscall (); + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, filedes) == -1) { - /* This is the child. */ - gcry_control (GCRYCTL_TERM_SECMEM); - es_fclose (infp); - es_fclose (outfp); - es_fclose (errfp); - do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], - except, flags); - /*NOTREACHED*/ + err = gpg_err_code_from_syserror (); + filedes[0] = filedes[1] = -1; } + post_syscall (); - /* This is the parent. */ - if (inpipe[0] != -1) - close (inpipe[0]); - if (outpipe[1] != -1) - close (outpipe[1]); - if (errpipe[1] != -1) - close (errpipe[1]); + return err; +} - if (r_infp) - *r_infp = infp; - if (r_outfp) - *r_outfp = outfp; - if (r_errfp) - *r_errfp = errfp; +static int +posix_open_null (int for_write) +{ + int fd; - return 0; + fd = open ("/dev/null", for_write? O_WRONLY : O_RDONLY); + if (fd == -1) + log_fatal ("failed to open '/dev/null': %s\n", strerror (errno)); + return fd; } +static void +call_spawn_cb (struct spawn_cb_arg *sca, + int fd_in, int fd_out, int fd_err, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) +{ + sca->fds[0] = fd_in; + sca->fds[1] = fd_out; + sca->fds[2] = fd_err; + sca->except_fds = NULL; + sca->arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (sca); +} +static void +my_exec (const char *pgmname, const char *argv[], struct spawn_cb_arg *sca) +{ + int i; -/* Simplified version of gnupg_spawn_process. This function forks and - then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout - and ERRFD to stderr (any of them may be -1 to connect them to - /dev/null). The arguments for the process are expected in the NULL - terminated array ARGV. The program name itself should not be - included there. Calling gnupg_wait_process is required. + /* Assign /dev/null to unused FDs. */ + for (i = 0; i <= 2; i++) + if (sca->fds[i] == -1) + sca->fds[i] = posix_open_null (i); - Returns 0 on success or an error code. */ -gpg_error_t -gnupg_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, pid_t *pid) + /* Connect the standard files. */ + for (i = 0; i <= 2; i++) + if (sca->fds[i] != i) + { + if (dup2 (sca->fds[i], i) == -1) + log_fatal ("dup2 std%s failed: %s\n", + i==0?"in":i==1?"out":"err", strerror (errno)); + /* + * We don't close sca.fds[i] here, but close them by + * close_all_fds. Note that there may be same one in three of + * sca->fds[i]. + */ + } + + /* Close all other files. */ + close_all_fds (3, sca->except_fds); + + execv (pgmname, (char *const *)argv); + /* No way to print anything, as we have may have closed all streams. */ + _exit (127); +} + +static gpg_err_code_t +spawn_detached (const char *pgmname, const char *argv[], + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) { - gpg_error_t err; + gpg_err_code_t ec; + pid_t pid; - *pid = fork (); - if (*pid == (pid_t)(-1)) + /* FIXME: Is this GnuPG specific or should we keep it. */ + if (getuid() != geteuid()) { - err = my_error_from_syserror (); - log_error (_("error forking process: %s\n"), strerror (errno)); - return err; + xfree (argv); + return GPG_ERR_BUG; } - if (!*pid) + if (access (pgmname, X_OK)) { - gcry_control (GCRYCTL_TERM_SECMEM); - /* Run child. */ - do_exec (pgmname, argv, infd, outfd, errfd, NULL, 0); - /*NOTREACHED*/ + ec = gpg_err_code_from_syserror (); + xfree (argv); + return ec; } - return 0; -} - - - + pre_syscall (); + pid = fork (); + post_syscall (); + if (pid == (pid_t)(-1)) + { + ec = gpg_err_code_from_syserror (); + log_error (_("error forking process: %s\n"), gpg_strerror (ec)); + xfree (argv); + return ec; + } -/* Waiting for child processes. + if (!pid) + { + pid_t pid2; + struct spawn_cb_arg sca; - waitpid(2) may return information about terminated children that we - did not yet request, and there is no portable way to wait for a - specific set of children. + if (setsid() == -1 || chdir ("/")) + _exit (1); - As a workaround, we store the results of children for later use. + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ - XXX: This assumes that PIDs are not reused too quickly. */ + call_spawn_cb (&sca, -1, -1, -1, spawn_cb, spawn_cb_arg); -struct terminated_child -{ - pid_t pid; - int exitcode; - struct terminated_child *next; -}; + my_exec (pgmname, argv, &sca); + /*NOTREACHED*/ + } -struct terminated_child *terminated_children; + pre_syscall (); + if (waitpid (pid, NULL, 0) == -1) + { + post_syscall (); + ec = gpg_err_code_from_syserror (); + log_error ("waitpid failed in gpgrt_spawn_process_detached: %s", + gpg_strerror (ec)); + return ec; + } + else + post_syscall (); + return 0; +} -static gpg_error_t -store_result (pid_t pid, int exitcode) +void +gnupg_spawn_helper (struct spawn_cb_arg *sca) { - struct terminated_child *c; + int *user_except = sca->arg; + sca->except_fds = user_except; +} - c = xtrymalloc (sizeof *c); - if (c == NULL) - return gpg_err_code_from_syserror (); +gpg_err_code_t +gnupg_process_spawn (const char *pgmname, const char *argv1[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process) +{ + gpg_err_code_t ec; + gnupg_process_t process; + int fd_in[2]; + int fd_out[2]; + int fd_err[2]; + pid_t pid; + const char **argv; + int i, j; - c->pid = pid; - c->exitcode = exitcode; - c->next = terminated_children; - terminated_children = c; + check_syscall_func (); - return 0; -} + if (r_process) + *r_process = NULL; + /* Create the command line argument array. */ + i = 0; + if (argv1) + while (argv1[i]) + i++; + argv = xtrycalloc (i+2, sizeof *argv); + if (!argv) + return gpg_err_code_from_syserror (); + argv[0] = strrchr (pgmname, '/'); + if (argv[0]) + argv[0]++; + else + argv[0] = pgmname; -static int -get_result (pid_t pid, int *r_exitcode) -{ - struct terminated_child *c, **prevp; + if (argv1) + for (i=0, j=1; argv1[i]; i++, j++) + argv[j] = argv1[i]; - for (prevp = &terminated_children, c = terminated_children; - c; - prevp = &c->next, c = c->next) - if (c->pid == pid) - { - *prevp = c->next; - *r_exitcode = c->exitcode; - xfree (c); - return 1; - } + if ((flags & GNUPG_PROCESS_DETACHED)) + { + if ((flags & GNUPG_PROCESS_STDFDS_SETTING)) + { + xfree (argv); + return GPG_ERR_INV_FLAG; + } - return 0; -} + /* In detached case, it must be no R_PROCESS. */ + if (r_process) + { + xfree (argv); + return GPG_ERR_INV_ARG; + } + return spawn_detached (pgmname, argv, spawn_cb, spawn_cb_arg); + } -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) -{ - gpg_err_code_t ec; - int i, status; + process = xtrycalloc (1, sizeof (struct gnupg_process)); + if (process == NULL) + { + xfree (argv); + return gpg_err_code_from_syserror (); + } - if (r_exitcode) - *r_exitcode = -1; + process->pgmname = pgmname; + process->flags = flags; - if (pid == (pid_t)(-1)) - return gpg_error (GPG_ERR_INV_VALUE); + if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR)) + { + ec = do_create_socketpair (fd_in); + if (ec) + { + xfree (process); + xfree (argv); + return ec; + } + fd_out[0] = dup (fd_in[0]); + fd_out[1] = dup (fd_in[1]); + } + else + { + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + { + ec = do_create_pipe (fd_in); + if (ec) + { + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDIN_KEEP)) + { + fd_in[0] = 0; + fd_in[1] = -1; + } + else + { + fd_in[0] = -1; + fd_in[1] = -1; + } -#ifdef USE_NPTH - i = npth_waitpid (pid, &status, hang? 0:WNOHANG); -#else - while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) - && errno == EINTR); -#endif + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + { + ec = do_create_pipe (fd_out); + if (ec) + { + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + xfree (process); + xfree (argv); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDOUT_KEEP)) + { + fd_out[0] = -1; + fd_out[1] = 1; + } + else + { + fd_out[0] = -1; + fd_out[1] = -1; + } + } - if (i == (pid_t)(-1)) + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) { - ec = gpg_err_code_from_errno (errno); - log_error (_("waiting for process %d to terminate failed: %s\n"), - (int)pid, strerror (errno)); + ec = do_create_pipe (fd_err); + if (ec) + { + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + xfree (process); + xfree (argv); + return ec; + } } - else if (!i) + else if ((flags & GNUPG_PROCESS_STDERR_KEEP)) { - ec = GPG_ERR_TIMEOUT; /* Still running. */ + fd_err[0] = -1; + fd_err[1] = 2; } - else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + else { - log_error (_("error running '%s': probably not installed\n"), pgmname); - ec = GPG_ERR_CONFIGURATION; + fd_err[0] = -1; + fd_err[1] = -1; } - else if (WIFEXITED (status) && WEXITSTATUS (status)) + + pre_syscall (); + pid = fork (); + post_syscall (); + if (pid == (pid_t)(-1)) { - if (!r_exitcode) - log_error (_("error running '%s': exit status %d\n"), pgmname, - WEXITSTATUS (status)); - else - *r_exitcode = WEXITSTATUS (status); - ec = GPG_ERR_GENERAL; + ec = gpg_err_code_from_syserror (); + log_error (_("error forking process: %s\n"), gpg_strerror (ec)); + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + if (fd_err[0] >= 0) + close (fd_err[0]); + if (fd_err[1] >= 0 && fd_err[1] != 2) + close (fd_err[1]); + xfree (process); + xfree (argv); + return ec; } - else if (!WIFEXITED (status)) + + if (!pid) { - log_error (_("error running '%s': terminated\n"), pgmname); - ec = GPG_ERR_GENERAL; + struct spawn_cb_arg sca; + + if (fd_in[1] >= 0) + close (fd_in[1]); + if (fd_out[0] >= 0) + close (fd_out[0]); + if (fd_err[0] >= 0) + close (fd_err[0]); + + call_spawn_cb (&sca, fd_in[0], fd_out[1], fd_err[1], + spawn_cb, spawn_cb_arg); + + /* Run child. */ + my_exec (pgmname, argv, &sca); + /*NOTREACHED*/ } - else + + xfree (argv); + process->pid = pid; + + if (fd_in[0] >= 0 && fd_in[0] != 0) + close (fd_in[0]); + if (fd_out[1] >= 0 && fd_out[1] != 1) + close (fd_out[1]); + if (fd_err[1] >= 0 && fd_err[1] != 2) + close (fd_err[1]); + process->fd_in = fd_in[1]; + process->fd_out = fd_out[0]; + process->fd_err = fd_err[0]; + process->wstatus = -1; + process->terminated = 0; + + if (r_process == NULL) { - if (r_exitcode) - *r_exitcode = 0; - ec = 0; + ec = gnupg_process_wait (process, 1); + gnupg_process_release (process); + return ec; } - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); + *r_process = process; + return 0; } -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, - int hang, int *r_exitcodes) +static gpg_err_code_t +process_kill (gnupg_process_t process, int sig) { gpg_err_code_t ec = 0; - size_t i, left; - int *dummy = NULL; + pid_t pid = process->pid; + + pre_syscall (); + if (kill (pid, sig) < 0) + ec = gpg_err_code_from_syserror (); + post_syscall (); + return ec; +} + +gpg_err_code_t +gnupg_process_terminate (gnupg_process_t process) +{ + return process_kill (process, SIGTERM); +} - if (r_exitcodes == NULL) +gpg_err_code_t +gnupg_process_get_fds (gnupg_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) +{ + (void)flags; + if (r_fd_in) { - dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); - if (dummy == NULL) - return gpg_err_code_from_syserror (); + *r_fd_in = process->fd_in; + process->fd_in = -1; } - - for (i = 0, left = count; i < count; i++) + if (r_fd_out) + { + *r_fd_out = process->fd_out; + process->fd_out = -1; + } + if (r_fd_err) { - int status = -1; + *r_fd_err = process->fd_err; + process->fd_err = -1; + } - /* Skip invalid PID. */ - if (pids[i] == (pid_t)(-1)) - { - r_exitcodes[i] = -1; - left -= 1; - continue; - } + return 0; +} - /* See if there was a previously stored result for this pid. */ - if (get_result (pids[i], &status)) - left -= 1; +gpg_err_code_t +gnupg_process_get_streams (gnupg_process_t process, unsigned int flags, + gpgrt_stream_t *r_fp_in, gpgrt_stream_t *r_fp_out, + gpgrt_stream_t *r_fp_err) +{ + int nonblock = (flags & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0; - r_exitcodes[i] = status; + if (r_fp_in) + { + *r_fp_in = es_fdopen (process->fd_in, nonblock? "w,nonblock" : "w"); + process->fd_in = -1; } + if (r_fp_out) + { + *r_fp_out = es_fdopen (process->fd_out, nonblock? "r,nonblock" : "r"); + process->fd_out = -1; + } + if (r_fp_err) + { + *r_fp_err = es_fdopen (process->fd_err, nonblock? "r,nonblock" : "r"); + process->fd_err = -1; + } + return 0; +} - while (left > 0) +static gpg_err_code_t +process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr) +{ + switch (request) { - pid_t pid; - int status; + case GNUPG_PROCESS_NOP: + return 0; -#ifdef USE_NPTH - pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG); -#else - while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) - && errno == EINTR); -#endif + case GNUPG_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); - if (pid == (pid_t)(-1)) - { - ec = gpg_err_code_from_errno (errno); - log_error (_("waiting for processes to terminate failed: %s\n"), - strerror (errno)); - break; - } - else if (!pid) - { - ec = GPG_ERR_TIMEOUT; /* Still running. */ - break; - } - else - { - for (i = 0; i < count; i++) - if (pid == pids[i]) - break; + if (r_id == NULL) + return GPG_ERR_INV_VALUE; - if (i == count) - { - /* No match, store this result. */ - ec = store_result (pid, status); - if (ec) - break; - continue; - } + *r_id = (int)process->pid; + return 0; + } - /* Process PIDS[i] died. */ - if (r_exitcodes[i] != (pid_t) -1) - { - log_error ("PID %d was reused", pid); - ec = GPG_ERR_GENERAL; - break; - } + case GNUPG_PROCESS_GET_EXIT_ID: + { + int status = process->wstatus; + int *r_exit_status = va_arg (arg_ptr, int *); - left -= 1; - r_exitcodes[i] = status; - } - } + if (!process->terminated) + return GPG_ERR_UNFINISHED; - for (i = 0; i < count; i++) - { - if (r_exitcodes[i] == -1) - continue; + if (WIFEXITED (status)) + { + if (r_exit_status) + *r_exit_status = WEXITSTATUS (status); + } + else + *r_exit_status = -1; - if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) - { - log_error (_("error running '%s': probably not installed\n"), - pgmnames[i]); - ec = GPG_ERR_CONFIGURATION; - } - else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i])) - { - if (dummy) - log_error (_("error running '%s': exit status %d\n"), - pgmnames[i], WEXITSTATUS (r_exitcodes[i])); - else - r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]); - ec = GPG_ERR_GENERAL; - } - else if (!WIFEXITED (r_exitcodes[i])) - { - log_error (_("error running '%s': terminated\n"), pgmnames[i]); - ec = GPG_ERR_GENERAL; - } + return 0; + } + + case GNUPG_PROCESS_GET_PID: + { + pid_t *r_pid = va_arg (arg_ptr, pid_t *); + + if (r_pid == NULL) + return GPG_ERR_INV_VALUE; + + *r_pid = process->pid; + return 0; + } + + case GNUPG_PROCESS_GET_WSTATUS: + { + int status = process->wstatus; + int *r_if_exited = va_arg (arg_ptr, int *); + int *r_if_signaled = va_arg (arg_ptr, int *); + int *r_exit_status = va_arg (arg_ptr, int *); + int *r_termsig = va_arg (arg_ptr, int *); + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (WIFEXITED (status)) + { + if (r_if_exited) + *r_if_exited = 1; + if (r_if_signaled) + *r_if_signaled = 0; + if (r_exit_status) + *r_exit_status = WEXITSTATUS (status); + if (r_termsig) + *r_termsig = 0; + } + else if (WIFSIGNALED (status)) + { + if (r_if_exited) + *r_if_exited = 0; + if (r_if_signaled) + *r_if_signaled = 1; + if (r_exit_status) + *r_exit_status = 0; + if (r_termsig) + *r_termsig = WTERMSIG (status); + } + + return 0; + } + + case GNUPG_PROCESS_KILL: + { + int sig = va_arg (arg_ptr, int); + + return process_kill (process, sig); + } + + default: + break; } - xfree (dummy); - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); + return GPG_ERR_UNKNOWN_COMMAND; } - - -void -gnupg_release_process (pid_t pid) +gpg_err_code_t +gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...) { - (void)pid; -} + va_list arg_ptr; + gpg_err_code_t ec; + va_start (arg_ptr, request); + ec = process_vctl (process, request, arg_ptr); + va_end (arg_ptr); + return ec; +} -/* Spawn a new process and immediately detach from it. The name of - the program to exec is PGMNAME and its arguments are in ARGV (the - programname is automatically passed as first argument). - Environment strings in ENVP are set. An error is returned if - pgmname is not executable; to make this work it is necessary to - provide an absolute file name. All standard file descriptors are - connected to /dev/null. */ -gpg_error_t -gnupg_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[] ) +gpg_err_code_t +gnupg_process_wait (gnupg_process_t process, int hang) { gpg_err_code_t ec; + int status; pid_t pid; - int i; - if (getuid() != geteuid()) - return my_error (GPG_ERR_BUG); + if (process->terminated) + /* Already terminated. */ + return 0; - if ((ec = gnupg_access (pgmname, X_OK))) - return gpg_err_make (default_errsource, ec); + pre_syscall (); + while ((pid = waitpid (process->pid, &status, hang? 0: WNOHANG)) + == (pid_t)(-1) && errno == EINTR); + post_syscall (); - pid = fork (); if (pid == (pid_t)(-1)) { - log_error (_("error forking process: %s\n"), strerror (errno)); - return my_error_from_syserror (); + ec = gpg_err_code_from_syserror (); + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, gpg_strerror (ec)); } - if (!pid) + else if (!pid) { - pid_t pid2; - - gcry_control (GCRYCTL_TERM_SECMEM); - if (setsid() == -1 || chdir ("/")) - _exit (1); - - pid2 = fork (); /* Double fork to let init take over the new child. */ - if (pid2 == (pid_t)(-1)) - _exit (1); - if (pid2) - _exit (0); /* Let the parent exit immediately. */ + ec = GPG_ERR_TIMEOUT; /* Still running. */ + } + else + { + process->terminated = 1; + process->wstatus = status; + ec = 0; + } - if (envp) - for (i=0; envp[i]; i++) - putenv (xstrdup (envp[i])); + return ec; +} - do_exec (pgmname, argv, -1, -1, -1, NULL, 0); +void +gnupg_process_release (gnupg_process_t process) +{ + if (!process) + return; - /*NOTREACHED*/ + if (process->terminated) + { + gnupg_process_terminate (process); + gnupg_process_wait (process, 1); } - if (waitpid (pid, NULL, 0) == -1) - log_error ("waitpid failed in gnupg_spawn_process_detached: %s", - strerror (errno)); - - return 0; + xfree (process); } - -/* Kill a process; that is send an appropriate signal to the process. - gnupg_wait_process must be called to actually remove the process - from the system. An invalid PID is ignored. */ -void -gnupg_kill_process (pid_t pid) +gpg_err_code_t +gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang) { - if (pid != (pid_t)(-1)) + gpg_err_code_t ec = 0; + int i; + + for (i = 0; i < count; i++) { - kill (pid, SIGTERM); + if (process_list[i]->terminated) + continue; + + ec = gnupg_process_wait (process_list[i], hang); + if (ec) + break; } + + return ec; } diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c index 2093f0d8b..08290e442 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -33,6 +33,8 @@ #if !defined(HAVE_W32_SYSTEM) #error This code is only used on W32. +#else +#define _WIN32_WINNT 0x600 #endif #include <stdio.h> @@ -63,9 +65,11 @@ #include "util.h" #include "i18n.h" #include "sysutils.h" +#define NEED_STRUCT_SPAWN_CB_ARG #include "exechelp.h" #include <windows.h> +#include <processthreadsapi.h> /* Define to 1 do enable debugging. */ #define DEBUG_W32_SPAWN 0 @@ -405,143 +409,147 @@ gnupg_close_pipe (int fd) if (fd != -1) close (fd); } + +struct gnupg_process { + const char *pgmname; + unsigned int terminated :1; /* or detached */ + unsigned int flags; + HANDLE hProcess; + HANDLE hd_in; + HANDLE hd_out; + HANDLE hd_err; + int exitcode; +}; + +static int gnupg_process_syscall_func_initialized; + +/* Functions called before and after blocking syscalls. */ +static void (*pre_syscall_func) (void); +static void (*post_syscall_func) (void); + +static void +check_syscall_func (void) +{ + if (!gnupg_process_syscall_func_initialized) + { + gpgrt_get_syscall_clamp (&pre_syscall_func, &post_syscall_func); + gnupg_process_syscall_func_initialized = 1; + } +} -/* Fork and exec the PGMNAME, see exechelp.h for details. */ -gpg_error_t -gnupg_spawn_process (const char *pgmname, const char *argv[], - int *except, unsigned int flags, - estream_t *r_infp, - estream_t *r_outfp, - estream_t *r_errfp, - pid_t *pid) +static void +pre_syscall (void) { - gpg_error_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFOW si; - int cr_flags; - char *cmdline; - wchar_t *wcmdline = NULL; - wchar_t *wpgmname = NULL; - HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; - estream_t infp = NULL; - estream_t outfp = NULL; - estream_t errfp = NULL; - HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, - INVALID_HANDLE_VALUE, - INVALID_HANDLE_VALUE}; - int i, rc; - es_syshd_t syshd; - gpg_err_source_t errsource = default_errsource; - int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); + if (pre_syscall_func) + pre_syscall_func (); +} + - (void)except; /* Not yet used. */ +static void +post_syscall (void) +{ + if (post_syscall_func) + post_syscall_func (); +} - if (r_infp) - *r_infp = NULL; - if (r_outfp) - *r_outfp = NULL; - if (r_errfp) - *r_errfp = NULL; - *pid = (pid_t)(-1); /* Always required. */ - if (r_infp) +/* + * Check if STARTUPINFOEXW supports PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + */ +static int +check_windows_version (void) +{ + static int is_vista_or_later = -1; + + OSVERSIONINFO osvi; + + if (is_vista_or_later == -1) { - if (create_inheritable_pipe (inpipe, INHERIT_READ)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } + memset (&osvi,0,sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx (&osvi); - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = inpipe[1]; - infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); - if (!infp) - { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - log_error (_("error creating a stream for a pipe: %s\n"), - gpg_strerror (err)); - CloseHandle (inpipe[0]); - CloseHandle (inpipe[1]); - inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE; - return err; - } + /* The feature is available on Vista or later. */ + is_vista_or_later = (osvi.dwMajorVersion >= 6); } - if (r_outfp) - { - if (create_inheritable_pipe (outpipe, INHERIT_WRITE)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } + return is_vista_or_later; +} - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = outpipe[0]; - outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!outfp) - { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - log_error (_("error creating a stream for a pipe: %s\n"), - gpg_strerror (err)); - CloseHandle (outpipe[0]); - CloseHandle (outpipe[1]); - outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; - if (infp) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; - } + +static gpg_err_code_t +spawn_detached (const char *pgmname, char *cmdline, + void (*spawn_cb) (struct spawn_cb_arg *), void *spawn_cb_arg) +{ + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOEXW si; + int cr_flags; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + gpg_err_code_t ec; + int ret; + struct spawn_cb_arg sca; + BOOL ask_inherit = FALSE; + + ec = gnupg_access (pgmname, X_OK); + if (ec) + { + xfree (cmdline); + return ec; } - if (r_errfp) + memset (&si, 0, sizeof si); + + sca.allow_foreground_window = FALSE; + sca.hd[0] = INVALID_HANDLE_VALUE; + sca.hd[1] = INVALID_HANDLE_VALUE; + sca.hd[2] = INVALID_HANDLE_VALUE; + sca.inherit_hds = NULL; + sca.arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (&sca); + + if (sca.inherit_hds) { - if (create_inheritable_pipe (errpipe, INHERIT_WRITE)) - { - err = gpg_err_make (errsource, GPG_ERR_GENERAL); - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - return err; - } + SIZE_T attr_list_size = 0; + HANDLE hd[16]; + HANDLE *hd_p = sca.inherit_hds; + int j = 0; - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = errpipe[0]; - errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); - if (!errfp) + if (hd_p) { - err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); - log_error (_("error creating a stream for a pipe: %s\n"), - gpg_strerror (err)); - CloseHandle (errpipe[0]); - CloseHandle (errpipe[1]); - errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (infp) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - return err; + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + log_error ("Too much handles\n"); + break; + } } + + if (j) + { + if (check_windows_version ()) + { + InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size); + si.lpAttributeList = xtrymalloc (attr_list_size); + if (si.lpAttributeList == NULL) + { + xfree (cmdline); + return gpg_err_code_from_syserror (); + } + InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0, + &attr_list_size); + UpdateProcThreadAttribute (si.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + hd, sizeof (HANDLE) * j, NULL, NULL); + } + + ask_inherit = TRUE; + } } /* Prepare security attributes. */ @@ -549,502 +557,683 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; - - if (inpipe[0] == INVALID_HANDLE_VALUE) - nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)? - GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0)); - if (outpipe[1] == INVALID_HANDLE_VALUE) - nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? - GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1)); - if (errpipe[1] == INVALID_HANDLE_VALUE) - nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDERR)? - GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1)); - - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE; - si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0]; - si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; - si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; + /* Start the process. */ + si.StartupInfo.cb = sizeof (si); + si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; cr_flags = (CREATE_DEFAULT_ERROR_MODE - | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) - | CREATE_SUSPENDED); - /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */ - /* pgmname, cmdline); */ + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); + /* Take care: CreateProcessW may modify wpgmname */ if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; + ret = 0; else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; + ret = 0; else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - TRUE, /* Inherit handles. */ - cr_flags, /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); - if (!rc) + ret = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + ask_inherit, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + (STARTUPINFOW *)&si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!ret) { if (!wpgmname || !wcmdline) log_error ("CreateProcess failed (utf8_to_wchar): %s\n", strerror (errno)); else - log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + log_error ("CreateProcess(detached) failed: %d\n", + (int)GetLastError ()); xfree (wpgmname); xfree (wcmdline); xfree (cmdline); - if (infp) - es_fclose (infp); - else if (inpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - if (outfp) - es_fclose (outfp); - else if (outpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (errfp) - es_fclose (errfp); - else if (errpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[0]); - if (errpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[1]); - return gpg_err_make (errsource, GPG_ERR_GENERAL); + return GPG_ERR_GENERAL; } + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); xfree (wpgmname); xfree (wcmdline); xfree (cmdline); - cmdline = NULL; - /* Close the inherited handles to /dev/null. */ - for (i=0; i < DIM (nullhd); i++) - if (nullhd[i] != INVALID_HANDLE_VALUE) - CloseHandle (nullhd[i]); + /* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - /* Close the inherited ends of the pipes. */ - if (inpipe[0] != INVALID_HANDLE_VALUE) - CloseHandle (inpipe[0]); - if (outpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (outpipe[1]); - if (errpipe[1] != INVALID_HANDLE_VALUE) - CloseHandle (errpipe[1]); - - /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ - /* " dwProcessID=%d dwThreadId=%d\n", */ - /* pi.hProcess, pi.hThread, */ - /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ + /* Note: AllowSetForegroundWindow doesn't make sense for background + process. */ - /* Fixme: For unknown reasons AllowSetForegroundWindow returns an - invalid argument error if we pass it the correct processID. As a - workaround we use -1 (ASFW_ANY). */ - if ((flags & GNUPG_SPAWN_RUN_ASFW)) - gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); - - /* Process has been created suspended; resume it now. */ - ResumeThread (pi.hThread); CloseHandle (pi.hThread); - - if (r_infp) - *r_infp = infp; - if (r_outfp) - *r_outfp = outfp; - if (r_errfp) - *r_errfp = errfp; - - *pid = handle_to_pid (pi.hProcess); + CloseHandle (pi.hProcess); return 0; - } +void +gnupg_spawn_helper (struct spawn_cb_arg *sca) +{ + HANDLE *user_except = sca->arg; + sca->inherit_hds = user_except; +} -/* Simplified version of gnupg_spawn_process. This function forks and - then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout - and ERRFD to stderr (any of them may be -1 to connect them to - /dev/null). The arguments for the process are expected in the NULL - terminated array ARGV. The program name itself should not be - included there. Calling gnupg_wait_process is required. - - Returns 0 on success or an error code. */ -gpg_error_t -gnupg_spawn_process_fd (const char *pgmname, const char *argv[], - int infd, int outfd, int errfd, pid_t *pid) +gpg_err_code_t +gnupg_process_spawn (const char *pgmname, const char *argv[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process) { - gpg_error_t err; + gpg_err_code_t ec; + gnupg_process_t process; SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; - STARTUPINFOW si; + STARTUPINFOEXW si; + int cr_flags; char *cmdline; wchar_t *wcmdline = NULL; wchar_t *wpgmname = NULL; - int i, rc; - HANDLE stdhd[3]; + int ret; + HANDLE hd_in[2]; + HANDLE hd_out[2]; + HANDLE hd_err[2]; + struct spawn_cb_arg sca; + int i; + BOOL ask_inherit = FALSE; + + check_syscall_func (); + + /* Build the command line. */ + ec = build_w32_commandline (pgmname, argv, &cmdline); + if (ec) + return ec; + + if ((flags & GNUPG_PROCESS_DETACHED)) + { + if ((flags & GNUPG_PROCESS_STDFDS_SETTING)) + { + xfree (cmdline); + return GPG_ERR_INV_FLAG; + } - /* Setup return values. */ - *pid = (pid_t)(-1); + /* In detached case, it must be no R_PROCESS. */ + if (r_process) + { + xfree (cmdline); + return GPG_ERR_INV_ARG; + } + + return spawn_detached (pgmname, cmdline, spawn_cb, spawn_cb_arg); + } + + if (r_process) + *r_process = NULL; + + process = xtrymalloc (sizeof (struct gnupg_process)); + if (process == NULL) + { + xfree (cmdline); + return gpg_err_code_from_syserror (); + } + + process->pgmname = pgmname; + process->flags = flags; + + if ((flags & GNUPG_PROCESS_STDINOUT_SOCKETPAIR)) + { + xfree (process); + xfree (cmdline); + return GPG_ERR_NOT_SUPPORTED; + } + + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + { + ec = create_inheritable_pipe (hd_in, INHERIT_READ); + if (ec) + { + xfree (process); + xfree (cmdline); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDIN_KEEP)) + { + hd_in[0] = GetStdHandle (STD_INPUT_HANDLE); + hd_in[1] = INVALID_HANDLE_VALUE; + } + else + { + hd_in[0] = w32_open_null (0); + hd_in[1] = INVALID_HANDLE_VALUE; + } + + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + { + ec = create_inheritable_pipe (hd_out, INHERIT_WRITE); + if (ec) + { + if (hd_in[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[0]); + if (hd_in[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[1]); + xfree (process); + xfree (cmdline); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDOUT_KEEP)) + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = GetStdHandle (STD_OUTPUT_HANDLE); + } + else + { + hd_out[0] = INVALID_HANDLE_VALUE; + hd_out[1] = w32_open_null (1); + } + + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + { + ec = create_inheritable_pipe (hd_err, INHERIT_WRITE); + if (ec) + { + if (hd_in[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[0]); + if (hd_in[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_in[1]); + if (hd_out[0] != INVALID_HANDLE_VALUE) + CloseHandle (hd_out[0]); + if (hd_out[1] != INVALID_HANDLE_VALUE) + CloseHandle (hd_out[1]); + xfree (process); + xfree (cmdline); + return ec; + } + } + else if ((flags & GNUPG_PROCESS_STDERR_KEEP)) + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = GetStdHandle (STD_ERROR_HANDLE); + } + else + { + hd_err[0] = INVALID_HANDLE_VALUE; + hd_err[1] = w32_open_null (1); + } + + memset (&si, 0, sizeof si); + + sca.allow_foreground_window = FALSE; + sca.hd[0] = hd_in[0]; + sca.hd[1] = hd_out[1]; + sca.hd[2] = hd_err[1]; + sca.inherit_hds = NULL; + sca.arg = spawn_cb_arg; + if (spawn_cb) + (*spawn_cb) (&sca); + + i = 0; + if (sca.hd[0] != INVALID_HANDLE_VALUE) + i++; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + i++; + if (sca.hd[2] != INVALID_HANDLE_VALUE) + i++; + + if (i != 0 || sca.inherit_hds) + { + SIZE_T attr_list_size = 0; + HANDLE hd[16]; + HANDLE *hd_p = sca.inherit_hds; + int j = 0; + + if (sca.hd[0] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[0]; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[1]; + if (sca.hd[1] != INVALID_HANDLE_VALUE) + hd[j++] = sca.hd[2]; + if (hd_p) + { + while (*hd_p != INVALID_HANDLE_VALUE) + if (j < DIM (hd)) + hd[j++] = *hd_p++; + else + { + log_error ("Too much handles\n"); + break; + } + } + + if (j) + { + if (check_windows_version ()) + { + InitializeProcThreadAttributeList (NULL, 1, 0, &attr_list_size); + si.lpAttributeList = xtrymalloc (attr_list_size); + if (si.lpAttributeList == NULL) + { + if ((flags & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + xfree (process); + xfree (cmdline); + return gpg_err_code_from_syserror (); + } + InitializeProcThreadAttributeList (si.lpAttributeList, 1, 0, + &attr_list_size); + UpdateProcThreadAttribute (si.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + hd, sizeof (HANDLE) * j, NULL, NULL); + } + ask_inherit = TRUE; + } + } /* Prepare security attributes. */ memset (&sec_attr, 0, sizeof sec_attr ); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; - - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; - stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; - stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; - stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; - si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd); - si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd); - si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd); - -/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ - /* Take care: CreateProcessW may modify wpgmname */ + /* Start the process. */ + si.StartupInfo.cb = sizeof (si); + si.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.StartupInfo.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE; + si.StartupInfo.hStdInput = sca.hd[0]; + si.StartupInfo.hStdOutput = sca.hd[1]; + si.StartupInfo.hStdError = sca.hd[2]; + + /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED); if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; + ret = 0; else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; + ret = 0; else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - TRUE, /* Inherit handles. */ - (CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ()) - | CREATE_SUSPENDED | DETACHED_PROCESS), - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); - if (!rc) + ret = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + ask_inherit, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + (STARTUPINFOW *)&si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!ret) { if (!wpgmname || !wcmdline) log_error ("CreateProcess failed (utf8_to_wchar): %s\n", - strerror (errno)); + strerror (errno)); else - log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); - err = my_error (GPG_ERR_GENERAL); + log_error ("CreateProcess failed: ec=%d\n", + (int)GetLastError ()); + if ((flags & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDIN_PIPE)) + CloseHandle (hd_in[1]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE)) + CloseHandle (hd_out[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE)) + CloseHandle (hd_err[0]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + xfree (wpgmname); + xfree (wcmdline); + xfree (process); + xfree (cmdline); + return GPG_ERR_GENERAL; } - else - err = 0; + + if (si.lpAttributeList) + DeleteProcThreadAttributeList (si.lpAttributeList); xfree (wpgmname); xfree (wcmdline); xfree (cmdline); - for (i=0; i < 3; i++) - if (stdhd[i] != INVALID_HANDLE_VALUE) - CloseHandle (stdhd[i]); - if (err) - return err; -/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ -/* " dwProcessID=%d dwThreadId=%d\n", */ -/* pi.hProcess, pi.hThread, */ -/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + if ((flags & GNUPG_PROCESS_STDIN_PIPE) + || !(flags & GNUPG_PROCESS_STDIN_KEEP)) + CloseHandle (hd_in[0]); + if ((flags & GNUPG_PROCESS_STDOUT_PIPE) + || !(flags & GNUPG_PROCESS_STDOUT_KEEP)) + CloseHandle (hd_out[1]); + if ((flags & GNUPG_PROCESS_STDERR_PIPE) + || !(flags & GNUPG_PROCESS_STDERR_KEEP)) + CloseHandle (hd_err[1]); + + /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + if (sca.allow_foreground_window) + { + /* Fixme: For unknown reasons AllowSetForegroundWindow returns + * an invalid argument error if we pass it the correct + * processID. As a workaround we use -1 (ASFW_ANY). */ + if (!AllowSetForegroundWindow (ASFW_ANY /*pi.dwProcessId*/)) + log_info ("AllowSetForegroundWindow() failed: ec=%d\n", + (int)GetLastError ()); + } /* Process has been created suspended; resume it now. */ + pre_syscall (); ResumeThread (pi.hThread); CloseHandle (pi.hThread); + post_syscall (); - *pid = handle_to_pid (pi.hProcess); - return 0; + process->hProcess = pi.hProcess; + process->hd_in = hd_in[1]; + process->hd_out = hd_out[0]; + process->hd_err = hd_err[0]; + process->exitcode = -1; + process->terminated = 0; + if (r_process == NULL) + { + ec = gnupg_process_wait (process, 1); + gnupg_process_release (process); + return ec; + } + + *r_process = process; + return 0; } +gpg_err_code_t +gnupg_process_get_fds (gnupg_process_t process, unsigned int flags, + int *r_fd_in, int *r_fd_out, int *r_fd_err) +{ + (void)flags; + if (r_fd_in) + { + *r_fd_in = _open_osfhandle ((intptr_t)process->hd_in, O_APPEND); + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_fd_out) + { + *r_fd_out = _open_osfhandle ((intptr_t)process->hd_out, O_RDONLY); + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_fd_err) + { + *r_fd_err = _open_osfhandle ((intptr_t)process->hd_err, O_RDONLY); + process->hd_err = INVALID_HANDLE_VALUE; + } + + return 0; +} -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +gpg_err_code_t +gnupg_process_get_streams (gnupg_process_t process, unsigned int flags, + estream_t *r_fp_in, estream_t *r_fp_out, + estream_t *r_fp_err) { - return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode); + int nonblock = (flags & GNUPG_PROCESS_STREAM_NONBLOCK)? 1: 0; + es_syshd_t syshd; + + syshd.type = ES_SYSHD_HANDLE; + if (r_fp_in) + { + syshd.u.handle = process->hd_in; + *r_fp_in = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_fp_out) + { + syshd.u.handle = process->hd_out; + *r_fp_out = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_fp_err) + { + syshd.u.handle = process->hd_err; + *r_fp_err = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + process->hd_err = INVALID_HANDLE_VALUE; + } + return 0; } -/* See exechelp.h for a description. */ -gpg_error_t -gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, - int hang, int *r_exitcodes) +static gpg_err_code_t +process_kill (gnupg_process_t process, unsigned int exitcode) { gpg_err_code_t ec = 0; - size_t i; - HANDLE *procs; - int code; - procs = xtrycalloc (count, sizeof *procs); - if (procs == NULL) - return my_error_from_syserror (); + pre_syscall (); + if (TerminateProcess (process->hProcess, exitcode)) + ec = gpg_err_code_from_syserror (); + post_syscall (); + return ec; +} - for (i = 0; i < count; i++) +static gpg_err_code_t +process_vctl (gnupg_process_t process, unsigned int request, va_list arg_ptr) +{ + switch (request) { - if (r_exitcodes) - r_exitcodes[i] = -1; + case GNUPG_PROCESS_NOP: + return 0; - if (pids[i] == (pid_t)(-1)) - return my_error (GPG_ERR_INV_VALUE); + case GNUPG_PROCESS_GET_PROC_ID: + { + int *r_id = va_arg (arg_ptr, int *); - procs[i] = pid_to_handle (pids[i]); - } + if (r_id == NULL) + return GPG_ERR_INV_VALUE; - /* FIXME: We should do a pth_waitpid here. However this has not yet - been implemented. A special W32 pth system call would even be - better. */ - code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0); - switch (code) - { - case WAIT_TIMEOUT: - ec = GPG_ERR_TIMEOUT; - goto leave; + *r_id = (int)GetProcessId (process->hProcess); + return 0; + } - case WAIT_FAILED: - log_error (_("waiting for processes to terminate failed: %s\n"), - w32_strerror (-1)); - ec = GPG_ERR_GENERAL; - goto leave; + case GNUPG_PROCESS_GET_EXIT_ID: + { + int *r_exit_status = va_arg (arg_ptr, int *); + unsigned long exit_code; - case WAIT_OBJECT_0: - for (i = 0; i < count; i++) - { - DWORD exc; + *r_exit_status = -1; - if (! GetExitCodeProcess (procs[i], &exc)) - { - log_error (_("error getting exit code of process %d: %s\n"), - (int) pids[i], w32_strerror (-1) ); - ec = GPG_ERR_GENERAL; - } - else if (exc) - { - if (!r_exitcodes) - log_error (_("error running '%s': exit status %d\n"), - pgmnames[i], (int)exc); - else - r_exitcodes[i] = (int)exc; - ec = GPG_ERR_GENERAL; - } - else - { - if (r_exitcodes) - r_exitcodes[i] = 0; - } - } - break; + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + + if (GetExitCodeProcess (process->hProcess, &exit_code) == 0) + return gpg_err_code_from_syserror (); + + *r_exit_status = (int)exit_code; + return 0; + } + + case GNUPG_PROCESS_GET_P_HANDLE: + { + HANDLE *r_hProcess = va_arg (arg_ptr, HANDLE *); + + if (r_hProcess == NULL) + return GPG_ERR_INV_VALUE; + + *r_hProcess = process->hProcess; + process->hProcess = INVALID_HANDLE_VALUE; + return 0; + } + + case GNUPG_PROCESS_GET_HANDLES: + { + HANDLE *r_hd_in = va_arg (arg_ptr, HANDLE *); + HANDLE *r_hd_out = va_arg (arg_ptr, HANDLE *); + HANDLE *r_hd_err = va_arg (arg_ptr, HANDLE *); + + if (r_hd_in) + { + *r_hd_in = process->hd_in; + process->hd_in = INVALID_HANDLE_VALUE; + } + if (r_hd_out) + { + *r_hd_out = process->hd_out; + process->hd_out = INVALID_HANDLE_VALUE; + } + if (r_hd_err) + { + *r_hd_err = process->hd_err; + process->hd_err = INVALID_HANDLE_VALUE; + } + return 0; + } + + case GNUPG_PROCESS_GET_EXIT_CODE: + { + unsigned long *r_exitcode = va_arg (arg_ptr, unsigned long *); + + if (!process->terminated) + return GPG_ERR_UNFINISHED; + + if (process->hProcess == INVALID_HANDLE_VALUE) + { + *r_exitcode = (unsigned long)-1; + return 0; + } + + if (GetExitCodeProcess (process->hProcess, r_exitcode) == 0) + return gpg_err_code_from_syserror (); + return 0; + } + + case GNUPG_PROCESS_KILL_WITH_EC: + { + unsigned int exitcode = va_arg (arg_ptr, unsigned int); + + if (process->terminated) + return 0; + + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; + + return process_kill (process, exitcode); + } default: - log_error ("WaitForMultipleObjects returned unexpected " - "code %d\n", code); - ec = GPG_ERR_GENERAL; break; } - leave: - return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); + return GPG_ERR_UNKNOWN_COMMAND; } - - -void -gnupg_release_process (pid_t pid) +gpg_err_code_t +gnupg_process_ctl (gnupg_process_t process, unsigned int request, ...) { - if (pid != (pid_t)INVALID_HANDLE_VALUE) - { - HANDLE process = (HANDLE)pid; + va_list arg_ptr; + gpg_err_code_t ec; - CloseHandle (process); - } + va_start (arg_ptr, request); + ec = process_vctl (process, request, arg_ptr); + va_end (arg_ptr); + return ec; } - -/* Spawn a new process and immediately detach from it. The name of - the program to exec is PGMNAME and its arguments are in ARGV (the - programname is automatically passed as first argument). - Environment strings in ENVP are set. An error is returned if - pgmname is not executable; to make this work it is necessary to - provide an absolute file name. All standard file descriptors are - connected to /dev/null. */ -gpg_error_t -gnupg_spawn_process_detached (const char *pgmname, const char *argv[], - const char *envp[] ) +gpg_err_code_t +gnupg_process_wait (gnupg_process_t process, int hang) { - gpg_error_t err; - SECURITY_ATTRIBUTES sec_attr; - PROCESS_INFORMATION pi = - { - NULL, /* Returns process handle. */ - 0, /* Returns primary thread handle. */ - 0, /* Returns pid. */ - 0 /* Returns tid. */ - }; - STARTUPINFOW si; - int cr_flags; - char *cmdline; - wchar_t *wcmdline = NULL; - wchar_t *wpgmname = NULL; - BOOL in_job = FALSE; gpg_err_code_t ec; - int rc; - int jobdebug; + int code; - /* We don't use ENVP. */ - (void)envp; + if (process->hProcess == INVALID_HANDLE_VALUE) + return 0; - cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS"); - jobdebug = (cmdline && (atoi (cmdline) & 1)); + pre_syscall (); + code = WaitForSingleObject (process->hProcess, hang? INFINITE : 0); + post_syscall (); - if ((ec = gnupg_access (pgmname, X_OK))) - return gpg_err_make (default_errsource, ec); + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; - /* Prepare security attributes. */ - memset (&sec_attr, 0, sizeof sec_attr ); - sec_attr.nLength = sizeof sec_attr; - sec_attr.bInheritHandle = FALSE; + case WAIT_FAILED: + log_error (_("waiting for process to terminate failed: ec=%d\n"), + (int)GetLastError ()); + ec = GPG_ERR_GENERAL; + break; - /* Build the command line. */ - err = build_w32_commandline (pgmname, argv, &cmdline); - if (err) - return err; + case WAIT_OBJECT_0: + process->terminated = 1; + ec = 0; + break; - /* Start the process. */ - memset (&si, 0, sizeof si); - si.cb = sizeof (si); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + default: + log_debug ("WaitForSingleObject returned unexpected code %d\n", code); + ec = GPG_ERR_GENERAL; + break; + } - cr_flags = (CREATE_DEFAULT_ERROR_MODE - | GetPriorityClass (GetCurrentProcess ()) - | CREATE_NEW_PROCESS_GROUP - | DETACHED_PROCESS); + return ec; +} - /* Check if we were spawned as part of a Job. - * In a job we need to add CREATE_BREAKAWAY_FROM_JOB - * to the cr_flags, otherwise our child processes - * are killed when we terminate. */ - if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job)) - { - log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1)); - in_job = FALSE; - } +gpg_err_code_t +gnupg_process_terminate (gnupg_process_t process) +{ + return process_kill (process, 1); +} - if (in_job) - { - /* Only try to break away from job if it is allowed, otherwise - * CreateProcess() would fail with an "Access is denied" error. */ - JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; - if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, - &info, sizeof info, NULL)) - { - log_error ("QueryInformationJobObject() failed: %s\n", - w32_strerror (-1)); - } - else if ((info.BasicLimitInformation.LimitFlags & - JOB_OBJECT_LIMIT_BREAKAWAY_OK)) - { - if (jobdebug) - log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n"); - cr_flags |= CREATE_BREAKAWAY_FROM_JOB; - } - else if ((info.BasicLimitInformation.LimitFlags & - JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) - { - /* The child process should automatically detach from the job. */ - if (jobdebug) - log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; " - "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n"); - } - else - { - /* It seems that the child process must remain in the job. - * This is not necessarily an error, although it can cause premature - * termination of the child process when the job is closed. */ - if (jobdebug) - log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n"); - } - } - else - { - if (jobdebug) - log_debug ("Process is not in a Job\n"); - } +void +gnupg_process_release (gnupg_process_t process) +{ + if (!process) + return; - /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */ - /* pgmname, cmdline); */ - /* Take care: CreateProcessW may modify wpgmname */ - if (!(wpgmname = utf8_to_wchar (pgmname))) - rc = 0; - else if (!(wcmdline = utf8_to_wchar (cmdline))) - rc = 0; - else - rc = CreateProcessW (wpgmname, /* Program to start. */ - wcmdline, /* Command line arguments. */ - &sec_attr, /* Process security attributes. */ - &sec_attr, /* Thread security attributes. */ - FALSE, /* Inherit handles. */ - cr_flags, /* Creation flags. */ - NULL, /* Environment. */ - NULL, /* Use current drive/directory. */ - &si, /* Startup information. */ - &pi /* Returns process information. */ - ); - if (!rc) + if (process->terminated) { - if (!wpgmname || !wcmdline) - log_error ("CreateProcess failed (utf8_to_wchar): %s\n", - strerror (errno)); - else - log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); - xfree (wpgmname); - xfree (wcmdline); - xfree (cmdline); - return my_error (GPG_ERR_GENERAL); + gnupg_process_terminate (process); + gnupg_process_wait (process, 1); } - xfree (wpgmname); - xfree (wcmdline); - xfree (cmdline); - cmdline = NULL; - -/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ -/* " dwProcessID=%d dwThreadId=%d\n", */ -/* pi.hProcess, pi.hThread, */ -/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ - CloseHandle (pi.hThread); - CloseHandle (pi.hProcess); - - return 0; + CloseHandle (process->hProcess); + xfree (process); } - -/* Kill a process; that is send an appropriate signal to the process. - gnupg_wait_process must be called to actually remove the process - from the system. An invalid PID is ignored. */ -void -gnupg_kill_process (pid_t pid) +gpg_err_code_t +gnupg_process_wait_list (gnupg_process_t *process_list, int count, int hang) { - if (pid != (pid_t) INVALID_HANDLE_VALUE) + gpg_err_code_t ec = 0; + int i; + + for (i = 0; i < count; i++) { - HANDLE process = (HANDLE) pid; + if (process_list[i]->terminated) + continue; - /* Arbitrary error code. */ - TerminateProcess (process, 1); + ec = gnupg_process_wait (process_list[i], hang); + if (ec) + break; } + + return ec; } diff --git a/common/exechelp.h b/common/exechelp.h index 3343fe598..0370b23a4 100644 --- a/common/exechelp.h +++ b/common/exechelp.h @@ -73,140 +73,103 @@ gpg_error_t gnupg_create_pipe (int filedes[2]); void gnupg_close_pipe (int fd); -#define GNUPG_SPAWN_NONBLOCK 16 -#define GNUPG_SPAWN_RUN_ASFW 64 -#define GNUPG_SPAWN_DETACHED 128 -#define GNUPG_SPAWN_KEEP_STDIN 256 -#define GNUPG_SPAWN_KEEP_STDOUT 512 -#define GNUPG_SPAWN_KEEP_STDERR 1024 - -/* Fork and exec the program PGMNAME. - - If R_INFP is NULL connect stdin of the new process to /dev/null; if - it is not NULL store the address of a pointer to a new estream - there. If R_OUTFP is NULL connect stdout of the new process to - /dev/null; if it is not NULL store the address of a pointer to a - new estream there. If R_ERRFP is NULL connect stderr of the new - process to /dev/null; if it is not NULL store the address of a - pointer to a new estream there. On success the pid of the new - process is stored at PID. On error -1 is stored at PID and if - R_OUTFP or R_ERRFP are not NULL, NULL is stored there. - - The arguments for the process are expected in the NULL terminated - array ARGV. The program name itself should not be included there. - If PREEXEC is not NULL, the given function will be called right - before the exec. - - IF EXCEPT is not NULL, it is expected to be an ordered list of file - descriptors, terminated by an entry with the value (-1). These - file descriptors won't be closed before spawning a new program. - - Returns 0 on success or an error code. Calling gnupg_wait_process - and gnupg_release_process is required if the function succeeded. - - FLAGS is a bit vector: - - GNUPG_SPAWN_NONBLOCK - If set the two output streams are created in non-blocking - mode and the input stream is switched to non-blocking mode. - This is merely a convenience feature because the caller - could do the same with gpgrt_set_nonblock. Does not yet - work for Windows. - - GNUPG_SPAWN_DETACHED - If set the process will be started as a background process. - This flag is only useful under W32 (but not W32CE) systems, - so that no new console is created and pops up a console - window when starting the server. Does not work on W32CE. - - GNUPG_SPAWN_RUN_ASFW - On W32 (but not on W32CE) run AllowSetForegroundWindow for - the child. Note that due to unknown problems this actually - allows SetForegroundWindow for all children of this process. - - GNUPG_SPAWN_KEEP_STDIN - GNUPG_SPAWN_KEEP_STDOUT - GNUPG_SPAWN_KEEP_STDERR - Do not assign /dev/null to a non-required standard file - descriptor. - - */ -gpg_error_t -gnupg_spawn_process (const char *pgmname, const char *argv[], - int *execpt, unsigned int flags, - estream_t *r_infp, - estream_t *r_outfp, - estream_t *r_errfp, - pid_t *pid); - - -/* Simplified version of gnupg_spawn_process. This function forks and - then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout - and ERRFD to stderr (any of them may be -1 to connect them to - /dev/null). The arguments for the process are expected in the NULL - terminated array ARGV. The program name itself should not be - included there. Calling gnupg_wait_process and - gnupg_release_process is required. Returns 0 on success or an - error code. */ -gpg_error_t gnupg_spawn_process_fd (const char *pgmname, - const char *argv[], - int infd, int outfd, int errfd, - pid_t *pid); - - -/* If HANG is true, waits for the process identified by PID to exit; - if HANG is false, checks whether the process has terminated. - PGMNAME should be the same as supplied to the spawn function and is - only used for diagnostics. Return values: - - 0 - The process exited successful. 0 is stored at R_EXITCODE. - - GPG_ERR_GENERAL - The process exited without success. The exit code of process - is then stored at R_EXITCODE. An exit code of -1 indicates - that the process terminated abnormally (e.g. due to a signal). - - GPG_ERR_TIMEOUT - The process is still running (returned only if HANG is false). - - GPG_ERR_INV_VALUE - An invalid PID has been specified. - - Other error codes may be returned as well. Unless otherwise noted, - -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL - if the exit code is not required (in that case an error message will - be printed). Note that under Windows PID is not the process id but - the handle of the process. */ -gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang, - int *r_exitcode); - -/* Like gnupg_wait_process, but for COUNT processes. */ -gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids, - size_t count, int hang, int *r_exitcodes); - - -/* Kill a process; that is send an appropriate signal to the process. - gnupg_wait_process must be called to actually remove the process - from the system. An invalid PID is ignored. */ -void gnupg_kill_process (pid_t pid); - -/* Release the process identified by PID. This function is actually - only required for Windows but it does not harm to always call it. - It is a nop if PID is invalid. */ -void gnupg_release_process (pid_t pid); - - -/* Spawn a new process and immediately detach from it. The name of - the program to exec is PGMNAME and its arguments are in ARGV (the - programname is automatically passed as first argument). - Environment strings in ENVP are set. An error is returned if - pgmname is not executable; to make this work it is necessary to - provide an absolute file name. */ -gpg_error_t gnupg_spawn_process_detached (const char *pgmname, - const char *argv[], - const char *envp[] ); - +/* The opaque type for a subprocess. */ +typedef struct gnupg_process *gnupg_process_t; +#ifdef HAVE_W32_SYSTEM +struct spawn_cb_arg; +#ifdef NEED_STRUCT_SPAWN_CB_ARG +struct spawn_cb_arg { + HANDLE hd[3]; + HANDLE *inherit_hds; + BOOL allow_foreground_window; + void *arg; +}; +#endif +#else +struct spawn_cb_arg { + int fds[3]; + int *except_fds; + void *arg; +}; +#endif + +#define GNUPG_PROCESS_DETACHED (1 << 1) + +/* Specify how to keep/connect standard fds. */ +#define GNUPG_PROCESS_STDIN_PIPE (1 << 8) +#define GNUPG_PROCESS_STDOUT_PIPE (1 << 9) +#define GNUPG_PROCESS_STDERR_PIPE (1 << 10) +#define GNUPG_PROCESS_STDINOUT_SOCKETPAIR (1 << 11) +#define GNUPG_PROCESS_STDIN_KEEP (1 << 12) +#define GNUPG_PROCESS_STDOUT_KEEP (1 << 13) +#define GNUPG_PROCESS_STDERR_KEEP (1 << 14) +#define GNUPG_PROCESS_STDFDS_SETTING ( GNUPG_PROCESS_STDIN_PIPE \ + | GNUPG_PROCESS_STDOUT_PIPE | GNUPG_PROCESS_STDERR_PIPE \ + | GNUPG_PROCESS_STDINOUT_SOCKETPAIR | GNUPG_PROCESS_STDIN_KEEP \ + | GNUPG_PROCESS_STDOUT_KEEP | GNUPG_PROCESS_STDERR_KEEP) + +#define GNUPG_PROCESS_STREAM_NONBLOCK (1 << 16) + +/* Spawn helper. */ +void gnupg_spawn_helper (struct spawn_cb_arg *sca); + +/* Spawn PGMNAME. */ +gpg_err_code_t gnupg_process_spawn (const char *pgmname, const char *argv[], + unsigned int flags, + void (*spawn_cb) (struct spawn_cb_arg *), + void *spawn_cb_arg, + gnupg_process_t *r_process); + +/* Get FDs for subprocess I/O. It is the caller which should care + FDs (closing FDs). */ +gpg_err_code_t gnupg_process_get_fds (gnupg_process_t process, + unsigned int flags, + int *r_fd_in, int *r_fd_out, + int *r_fd_err); + +/* Get STREAMs for subprocess I/O. It is the caller which should care + STREAMs (closing STREAMs). */ +gpg_err_code_t gnupg_process_get_streams (gnupg_process_t process, + unsigned int flags, + gpgrt_stream_t *r_fp_in, + gpgrt_stream_t *r_fp_out, + gpgrt_stream_t *r_fp_err); + +enum gnupg_process_requests + { + /* Portable requests */ + GNUPG_PROCESS_NOP = 0, + GNUPG_PROCESS_GET_PROC_ID = 1, + GNUPG_PROCESS_GET_EXIT_ID = 2, + + /* POSIX only */ + GNUPG_PROCESS_GET_PID = 16, + GNUPG_PROCESS_GET_WSTATUS = 17, + GNUPG_PROCESS_KILL = 18, + + /* Windows only */ + GNUPG_PROCESS_GET_P_HANDLE = 32, + GNUPG_PROCESS_GET_HANDLES = 33, + GNUPG_PROCESS_GET_EXIT_CODE = 34, + GNUPG_PROCESS_KILL_WITH_EC = 35 + }; + +/* Control of a process. */ +gpg_err_code_t gnupg_process_ctl (gnupg_process_t process, + unsigned int request, ...); + +/* Wait for a single PROCESS. */ +gpg_err_code_t gnupg_process_wait (gnupg_process_t process, int hang); + +/* Terminate a PROCESS. */ +gpg_err_code_t gnupg_process_terminate (gnupg_process_t process); + +/* Release PROCESS resources. */ +void gnupg_process_release (gnupg_process_t process); + +/* Wait for a multiple processes. */ +gpg_err_code_t gnupg_process_wait_list (gnupg_process_t *process_list, + int count, int hang); #endif /*GNUPG_COMMON_EXECHELP_H*/ diff --git a/common/exectool.c b/common/exectool.c index aaf5898d0..3505c25f1 100644 --- a/common/exectool.c +++ b/common/exectool.c @@ -38,10 +38,14 @@ #include <gpg-error.h> #include <assuan.h> + #include "i18n.h" #include "logging.h" #include "membuf.h" #include "mischelp.h" +#ifdef HAVE_W32_SYSTEM +#define NEED_STRUCT_SPAWN_CB_ARG 1 +#endif #include "exechelp.h" #include "sysutils.h" #include "util.h" @@ -301,7 +305,6 @@ copy_buffer_flush (struct copy_buffer *c, estream_t sink) } - /* Run the program PGMNAME with the command line arguments given in * the NULL terminates array ARGV. If INPUT is not NULL it will be * fed to stdin of the process. stderr is logged using log_info and @@ -321,7 +324,7 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], void *status_cb_value) { gpg_error_t err; - pid_t pid = (pid_t) -1; + gnupg_process_t proc = NULL; estream_t infp = NULL; estream_t extrafp = NULL; estream_t outfp = NULL, errfp = NULL; @@ -335,7 +338,6 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], read_and_log_buffer_t fderrstate; struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL; int quiet = 0; - int dummy_exitcode; memset (fds, 0, sizeof fds); memset (&fderrstate, 0, sizeof fderrstate); @@ -411,10 +413,15 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], else exceptclose[0] = -1; - err = gnupg_spawn_process (pgmname, argv, - exceptclose, GNUPG_SPAWN_NONBLOCK, - input? &infp : NULL, - &outfp, &errfp, &pid); + err = gnupg_process_spawn (pgmname, argv, + ((input + ? GNUPG_PROCESS_STDIN_PIPE + : 0) + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + gnupg_spawn_helper, exceptclose, &proc); + gnupg_process_get_streams (proc, GNUPG_PROCESS_STREAM_NONBLOCK, + input? &infp : NULL, &outfp, &errfp); if (extrapipe[0] != -1) close (extrapipe[0]); if (argsave) @@ -546,20 +553,25 @@ gnupg_exec_tool_stream (const char *pgmname, const char *argv[], es_fclose (outfp); outfp = NULL; es_fclose (errfp); errfp = NULL; - err = gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); - pid = (pid_t)(-1); + err = gnupg_process_wait (proc, 1); + if (!err) + { /* To be compatible to old wait_process. */ + int status; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &status); + if (status) + err = gpg_error (GPG_ERR_GENERAL); + } leave: - if (err && pid != (pid_t) -1) - gnupg_kill_process (pid); + if (err && proc) + gnupg_process_terminate (proc); es_fclose (infp); es_fclose (extrafp); es_fclose (outfp); es_fclose (errfp); - if (pid != (pid_t)(-1)) - gnupg_wait_process (pgmname, pid, 1, quiet? &dummy_exitcode : NULL); - gnupg_release_process (pid); + gnupg_process_release (proc); copy_buffer_shred (cpbuf_in); xfree (cpbuf_in); diff --git a/common/get-passphrase.c b/common/get-passphrase.c index c24b40e88..8ea822710 100644 --- a/common/get-passphrase.c +++ b/common/get-passphrase.c @@ -94,7 +94,8 @@ start_agent (void) agentargs.lc_ctype, agentargs.lc_messages, agentargs.session_env, - 1, agentargs.verbosity, 0, NULL, NULL); + ASSHELP_FLAG_AUTOSTART, + agentargs.verbosity, 0, NULL, NULL); if (!err) { /* Tell the agent that we support Pinentry notifications. No diff --git a/common/homedir.c b/common/homedir.c index 6f99f3eab..deb6f3616 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,7 +1,7 @@ /* homedir.c - Setup the home directory. * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. * Copyright (C) 2013, 2016 Werner Koch - * Copyright (C) 2021 g10 Code GmbH + * Copyright (C) 2021, 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -93,6 +93,22 @@ static char *the_gnupg_homedir; static byte non_default_homedir; +/* An object to store information taken from a gpgconf.ctl file. This + * is parsed early at startup time and never changed later. */ +static struct +{ + unsigned int checked:1; /* True if we have checked for a gpgconf.ctl. */ + unsigned int found:1; /* True if a gpgconf.ctl was found. */ + unsigned int empty:1; /* The file is empty except for comments. */ + unsigned int valid:1; /* The entries in gpgconf.ctl are valid. */ + unsigned int portable:1;/* Windows portable installation. */ + char *gnupg; /* The "gnupg" directory part. */ + char *rootdir; /* rootdir or NULL */ + char *sysconfdir; /* sysconfdir or NULL */ + char *socketdir; /* socketdir or NULL */ +} gpgconf_ctl; + + #ifdef HAVE_W32_SYSTEM /* A flag used to indicate that a control file for gpgconf has been * detected. Under Windows the presence of this file indicates a @@ -119,6 +135,87 @@ static byte w32_bin_is_bin; static const char *w32_rootdir (void); #endif +/* Return the name of the gnupg dir. This is usually "gnupg". */ +static const char * +my_gnupg_dirname (void) +{ + if (gpgconf_ctl.valid && gpgconf_ctl.gnupg) + return gpgconf_ctl.gnupg; + return "gnupg"; +} + +/* Return the hardwired home directory which is not anymore so + * hardwired because it may now be modified using the gpgconf.ctl + * "gnupg" keyword. */ +static const char * +my_fixed_default_homedir (void) +{ + if (gpgconf_ctl.valid && gpgconf_ctl.gnupg) + { + static char *name; + char *p; + + if (!name) + { + name = xmalloc (strlen (GNUPG_DEFAULT_HOMEDIR) + + strlen (gpgconf_ctl.gnupg) + 1); + strcpy (name, GNUPG_DEFAULT_HOMEDIR); + p = strrchr (name, '/'); + if (p) + p++; + else + p = name; + if (*p == '.') + p++; /* Keep a leading dot. */ + strcpy (p, gpgconf_ctl.gnupg); + gpgrt_annotate_leaked_object (name); + } + return name; + } + return GNUPG_DEFAULT_HOMEDIR; +} + + + +/* Under Windows we need to modify the standard registry key with the + * "gnupg" keyword from a gpgconf.ctl. */ +#ifdef HAVE_W32_SYSTEM +const char * +gnupg_registry_dir (void) +{ + if (gpgconf_ctl.valid && gpgconf_ctl.gnupg) + { + static char *name; + char *p; + + if (!name) + { + name = xmalloc (strlen (GNUPG_REGISTRY_DIR) + + strlen (gpgconf_ctl.gnupg) + 1); + strcpy (name, GNUPG_REGISTRY_DIR); + p = strrchr (name, '\\'); + if (p) + p++; + else + p = name; + strcpy (p, gpgconf_ctl.gnupg); + if (!strncmp (p, "gnupg", 5)) + { + /* Registry keys are case-insensitive and we use a + * capitalized version of gnupg by default. So, if the + * new value starts with "gnupg" we apply the usual + * capitalization for this first part. */ + p[0] = 'G'; + p[3] = 'P'; + p[4] = 'G'; + } + gpgrt_annotate_leaked_object (name); + } + return name; + } + return GNUPG_REGISTRY_DIR; +} +#endif /*HAVE_W32_SYSTEM*/ /* This is a helper function to load and call a Windows function from @@ -324,7 +421,7 @@ standard_homedir (void) NULL, 0); if (path) { - dir = xstrconcat (path, "\\gnupg", NULL); + dir = xstrconcat (path, "\\", my_gnupg_dirname (), NULL); xfree (path); gpgrt_annotate_leaked_object (dir); @@ -335,12 +432,12 @@ standard_homedir (void) } else - dir = GNUPG_DEFAULT_HOMEDIR; + dir = my_fixed_default_homedir (); } } return dir; #else/*!HAVE_W32_SYSTEM*/ - return GNUPG_DEFAULT_HOMEDIR; + return my_fixed_default_homedir (); #endif /*!HAVE_W32_SYSTEM*/ } @@ -374,7 +471,7 @@ default_homedir (void) * warning if the homedir has been taken from the * registry. */ tmp = read_w32_registry_string (NULL, - GNUPG_REGISTRY_DIR, + gnupg_registry_dir (), "HomeDir"); if (tmp && !*tmp) { @@ -399,7 +496,7 @@ default_homedir (void) #endif /*HAVE_W32_SYSTEM*/ if (!dir || !*dir) - dir = GNUPG_DEFAULT_HOMEDIR; + dir = my_fixed_default_homedir (); else { char *p; @@ -427,6 +524,234 @@ default_homedir (void) } +/* Return true if S can be inteprtated as true. This is uised for + * keywords in gpgconf.ctl. Spaces must have been trimmed. */ +static int +string_is_true (const char *s) +{ + return (atoi (s) + || !ascii_strcasecmp (s, "yes") + || !ascii_strcasecmp (s, "true") + || !ascii_strcasecmp (s, "fact")); +} + +/* This function is used to parse the gpgconf.ctl file and set the + * information ito the gpgconf_ctl structure. This is called once + * with the full filename of gpgconf.ctl. There are two callers: One + * used on Windows and one on Unix. No error return but diagnostics + * are printed. */ +static void +parse_gpgconf_ctl (const char *fname) +{ + gpg_error_t err; + char *p; + char *line; + size_t linelen; + ssize_t length; + estream_t fp; + const char *name; + int anyitem = 0; + int ignoreall = 0; + char *gnupgval = NULL; + char *rootdir = NULL; + char *sysconfdir = NULL; + char *socketdir = NULL; + + if (gpgconf_ctl.checked) + return; /* Just in case this is called a second time. */ + gpgconf_ctl.checked = 1; + gpgconf_ctl.found = 0; + gpgconf_ctl.valid = 0; + gpgconf_ctl.empty = 0; + + if (gnupg_access (fname, F_OK)) + return; /* No gpgconf.ctl file. */ + + /* log_info ("detected '%s'\n", buffer); */ + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_info ("error opening '%s': %s\n", fname, gpg_strerror (err)); + return; + } + gpgconf_ctl.found = 1; + + line = NULL; + linelen = 0; + while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0) + { + static const char *names[] = + { + "gnupg", + "rootdir", + "sysconfdir", + "socketdir", + "portable", + ".enable" + }; + int i; + size_t n; + + /* Strip NL and CR, if present. */ + while (length > 0 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = 0; + trim_spaces (line); + if (*line == '#' || !*line) + continue; + anyitem = 1; + + /* Find the keyword. */ + name = NULL; + p = NULL; + for (i=0; i < DIM (names); i++) + { + n = strlen (names[i]); + if (!strncmp (line, names[i], n)) + { + while (line[n] == ' ' || line[n] == '\t') + n++; + if (line[n] == '=') + { + name = names[i]; + p = line + n + 1; + break; + } + } + } + if (!name) + continue; /* Keyword not known. */ + + trim_spaces (p); + p = substitute_envvars (p); + if (!p) + { + err = gpg_error_from_syserror (); + log_info ("error getting %s from gpgconf.ctl: %s\n", + name, gpg_strerror (err)); + } + else if (!strcmp (name, ".enable")) + { + if (string_is_true (p)) + ; /* Yes, this file shall be used. */ + else + ignoreall = 1; /* No, this file shall be ignored. */ + xfree (p); + } + else if (!strcmp (name, "gnupg")) + { + xfree (gnupgval); + gnupgval = p; + } + else if (!strcmp (name, "sysconfdir")) + { + xfree (sysconfdir); + sysconfdir = p; + } + else if (!strcmp (name, "socketdir")) + { + xfree (socketdir); + socketdir = p; + } + else if (!strcmp (name, "rootdir")) + { + xfree (rootdir); + rootdir = p; + } + else if (!strcmp (name, "portable")) + { + gpgconf_ctl.portable = string_is_true (p); + xfree (p); + } + else /* Unknown keyword. */ + xfree (p); + } + if (es_ferror (fp)) + { + err = gpg_error_from_syserror (); + log_info ("error reading '%s': %s\n", fname, gpg_strerror (err)); + ignoreall = 1; /* Force all entries to invalid. */ + } + es_fclose (fp); + xfree (line); + + if (ignoreall) + ; /* Forced error. Note that .found is still set. */ + else if (gnupgval && (!*gnupgval || strpbrk (gnupgval, "/\\"))) + { + /* We don't allow a slash or backslash in the value because our + * code assumes this is a single directory name. */ + log_info ("invalid %s '%s' specified in gpgconf.ctl\n", + "gnupg", gnupgval); + } + else if (rootdir && (!*rootdir || *rootdir != '/')) + { + log_info ("invalid %s '%s' specified in gpgconf.ctl\n", + "rootdir", rootdir); + } + else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/')) + { + log_info ("invalid %s '%s' specified in gpgconf.ctl\n", + "sysconfdir", sysconfdir); + } + else if (socketdir && (!*socketdir || *socketdir != '/')) + { + log_info ("invalid %s '%s' specified in gpgconf.ctl\n", + "socketdir", socketdir); + } + else + { + if (gnupgval) + { + gpgconf_ctl.gnupg = gnupgval; + gpgrt_annotate_leaked_object (gpgconf_ctl.gnupg); + /* log_info ("want gnupg '%s'\n", dir); */ + } + if (rootdir) + { + while (*rootdir && rootdir[strlen (rootdir)-1] == '/') + rootdir[strlen (rootdir)-1] = 0; + gpgconf_ctl.rootdir = rootdir; + gpgrt_annotate_leaked_object (gpgconf_ctl.rootdir); + /* log_info ("want rootdir '%s'\n", dir); */ + } + if (sysconfdir) + { + while (*sysconfdir && sysconfdir[strlen (sysconfdir)-1] == '/') + sysconfdir[strlen (sysconfdir)-1] = 0; + gpgconf_ctl.sysconfdir = sysconfdir; + gpgrt_annotate_leaked_object (gpgconf_ctl.sysconfdir); + /* log_info ("want sysconfdir '%s'\n", sdir); */ + } + if (socketdir) + { + while (*socketdir && socketdir[strlen (socketdir)-1] == '/') + socketdir[strlen (socketdir)-1] = 0; + gpgconf_ctl.socketdir = socketdir; + gpgrt_annotate_leaked_object (gpgconf_ctl.socketdir); + /* log_info ("want socketdir '%s'\n", s2dir); */ + } + gpgconf_ctl.valid = 1; + } + + gpgconf_ctl.empty = !anyitem; + if (!gpgconf_ctl.valid) + { + /* Error reading some entries - clear them all. */ + xfree (gnupgval); + xfree (rootdir); + xfree (sysconfdir); + xfree (socketdir); + gpgconf_ctl.gnupg = NULL; + gpgconf_ctl.rootdir = NULL; + gpgconf_ctl.sysconfdir = NULL; + gpgconf_ctl.socketdir = NULL; + } +} + + + #ifdef HAVE_W32_SYSTEM /* Check whether gpgconf is installed and if so read the gpgconf.ctl file. */ @@ -439,17 +764,20 @@ check_portable_app (const char *dir) if (!gnupg_access (fname, F_OK)) { strcpy (fname + strlen (fname) - 3, "ctl"); - if (!gnupg_access (fname, F_OK)) + parse_gpgconf_ctl (fname); + if ((gpgconf_ctl.found && gpgconf_ctl.empty) + || (gpgconf_ctl.valid && gpgconf_ctl.portable)) { - /* gpgconf.ctl file found. Record this fact. */ + unsigned int flags; + + /* Classic gpgconf.ctl file found. This is a portable + * application. Note that if there are any items in that + * file we don't consider this a portable application unless + * the (later added) ".portable" keyword has also been + * seen. */ w32_portable_app = 1; - { - unsigned int flags; - log_get_prefix (&flags); - log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); - } - /* FIXME: We should read the file to detect special flags - and print a warning if we don't understand them */ + log_get_prefix (&flags); + log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); } } xfree (fname); @@ -528,28 +856,14 @@ w32_rootdir (void) static const char * unix_rootdir (enum wantdir_values wantdir) { - static int checked; - static char *dir; /* for the rootdir */ - static char *sdir; /* for the sysconfdir */ - static char *s2dir; /* for the socketdir */ - - if (!checked) + if (!gpgconf_ctl.checked) { char *p; char *buffer; size_t bufsize = 256-1; int nread; gpg_error_t err; - char *line; - size_t linelen; - ssize_t length; - estream_t fp; - char *rootdir; - char *sysconfdir; - char *socketdir; const char *name; - int ignoreall = 0; - int okay; for (;;) { @@ -589,7 +903,7 @@ unix_rootdir (enum wantdir_values wantdir) if (!*buffer) { xfree (buffer); - checked = 1; + gpgconf_ctl.checked = 1; return NULL; /* Error - assume no gpgconf.ctl. */ } @@ -597,197 +911,36 @@ unix_rootdir (enum wantdir_values wantdir) if (!p) { xfree (buffer); - checked = 1; + gpgconf_ctl.checked = 1; return NULL; /* Erroneous /proc - assume no gpgconf.ctl. */ } *p = 0; /* BUFFER has the directory. */ - if ((p = strrchr (buffer, '/'))) - { - /* Strip one part and expect the file below a bin dir. */ - *p = 0; - p = xstrconcat (buffer, "/bin/gpgconf.ctl", NULL); - xfree (buffer); - buffer = p; - } - else /* !p */ + if (!(p = strrchr (buffer, '/'))) { /* Installed in the root which is not a good idea. Assume * no gpgconf.ctl. */ xfree (buffer); - checked = 1; - return NULL; - } - - if (gnupg_access (buffer, F_OK)) - { - /* No gpgconf.ctl file. */ - xfree (buffer); - checked = 1; + gpgconf_ctl.checked = 1; return NULL; } - /* log_info ("detected '%s'\n", buffer); */ - fp = es_fopen (buffer, "r"); - if (!fp) - { - err = gpg_error_from_syserror (); - log_info ("error opening '%s': %s\n", buffer, gpg_strerror (err)); - xfree (buffer); - checked = 1; - return NULL; - } - - line = NULL; - linelen = 0; - rootdir = NULL; - sysconfdir = NULL; - socketdir = NULL; - while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0) - { - static const char *names[] = - { - "rootdir", - "sysconfdir", - "socketdir", - ".enable" - }; - int i; - size_t n; - - /* Strip NL and CR, if present. */ - while (length > 0 - && (line[length - 1] == '\n' || line[length - 1] == '\r')) - line[--length] = 0; - trim_spaces (line); - /* Find the stamement. */ - name = NULL; - for (i=0; i < DIM (names); i++) - { - n = strlen (names[i]); - if (!strncmp (line, names[i], n)) - { - while (line[n] == ' ' || line[n] == '\t') - n++; - if (line[n] == '=') - { - name = names[i]; - p = line + n + 1; - break; - } - } - } - if (!name) - continue; /* Statement not known. */ - trim_spaces (p); - p = substitute_envvars (p); - if (!p) - { - err = gpg_error_from_syserror (); - log_info ("error getting %s from gpgconf.ctl: %s\n", - name, gpg_strerror (err)); - } - else if (!strcmp (name, ".enable")) - { - if (atoi (p) - || !ascii_strcasecmp (p, "yes") - || !ascii_strcasecmp (p, "true") - || !ascii_strcasecmp (p, "fact")) - ; /* Yes, this file shall be used. */ - else - ignoreall = 1; /* No, this file shall be ignored. */ - xfree (p); - } - else if (!strcmp (name, "sysconfdir")) - { - xfree (sysconfdir); - sysconfdir = p; - } - else if (!strcmp (name, "socketdir")) - { - xfree (socketdir); - socketdir = p; - } - else - { - xfree (rootdir); - rootdir = p; - } - } - if (es_ferror (fp)) - { - err = gpg_error_from_syserror (); - log_info ("error reading '%s': %s\n", buffer, gpg_strerror (err)); - es_fclose (fp); - xfree (buffer); - xfree (line); - xfree (rootdir); - xfree (sysconfdir); - xfree (socketdir); - checked = 1; - return NULL; - } - es_fclose (fp); + /* Strip one part and expect the file below a bin dir. */ + *p = 0; + p = xstrconcat (buffer, "/bin/gpgconf.ctl", NULL); + xfree (buffer); + buffer = p; + parse_gpgconf_ctl (buffer); xfree (buffer); - xfree (line); - - okay = 0; - if (ignoreall) - ; - else if (!rootdir || !*rootdir || *rootdir != '/') - { - log_info ("invalid rootdir '%s' specified in gpgconf.ctl\n", rootdir); - } - else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/')) - { - log_info ("invalid sysconfdir '%s' specified in gpgconf.ctl\n", - sysconfdir); - } - else if (socketdir && (!*socketdir || *socketdir != '/')) - { - log_info ("invalid socketdir '%s' specified in gpgconf.ctl\n", - socketdir); - } - else - { - okay = 1; - while (*rootdir && rootdir[strlen (rootdir)-1] == '/') - rootdir[strlen (rootdir)-1] = 0; - dir = rootdir; - gpgrt_annotate_leaked_object (dir); - /* log_info ("want rootdir '%s'\n", dir); */ - if (sysconfdir) - { - while (*sysconfdir && sysconfdir[strlen (sysconfdir)-1] == '/') - sysconfdir[strlen (sysconfdir)-1] = 0; - sdir = sysconfdir; - gpgrt_annotate_leaked_object (sdir); - /* log_info ("want sysconfdir '%s'\n", sdir); */ - } - if (socketdir) - { - while (*socketdir && socketdir[strlen (socketdir)-1] == '/') - socketdir[strlen (socketdir)-1] = 0; - s2dir = socketdir; - gpgrt_annotate_leaked_object (s2dir); - /* log_info ("want socketdir '%s'\n", s2dir); */ - } - } - - if (!okay) - { - xfree (rootdir); - xfree (sysconfdir); - xfree (socketdir); - dir = sdir = s2dir = NULL; - } - checked = 1; } + if (!gpgconf_ctl.valid) + return NULL; /* No valid entries in gpgconf.ctl */ + switch (wantdir) { - case WANTDIR_ROOT: return dir; - case WANTDIR_SYSCONF: return sdir; - case WANTDIR_SOCKET: return s2dir; + case WANTDIR_ROOT: return gpgconf_ctl.rootdir; + case WANTDIR_SYSCONF: return gpgconf_ctl.sysconfdir; + case WANTDIR_SOCKET: return gpgconf_ctl.socketdir; } return NULL; /* Not reached. */ @@ -982,7 +1135,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) if (w32_portable_app) { - name = xstrconcat (w32_rootdir (), DIRSEP_S, "gnupg", NULL); + name = xstrconcat (w32_rootdir (), DIRSEP_S, my_gnupg_dirname (), NULL); } else { @@ -993,7 +1146,7 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) NULL, 0); if (path) { - name = xstrconcat (path, "\\gnupg", NULL); + name = xstrconcat (path, "\\", my_gnupg_dirname (), NULL); xfree (path); if (gnupg_access (name, F_OK)) gnupg_mkdir (name, "-rwx"); @@ -1101,10 +1254,11 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) }; int i; struct stat sb; - char prefixbuffer[19 + 1 + 20 + 6 + 1]; + char prefixbuffer[256]; const char *prefix; const char *s; char *name = NULL; + const char *gnupgname = my_gnupg_dirname (); *r_info = 0; @@ -1143,12 +1297,13 @@ _gnupg_socketdir_internal (int skip_checks, unsigned *r_info) goto leave; } - if (strlen (prefix) + 7 >= sizeof prefixbuffer) + if (strlen (prefix) + strlen (gnupgname) + 2 >= sizeof prefixbuffer) { *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ goto leave; } - strcat (prefixbuffer, "/gnupg"); + strcat (prefixbuffer, "/"); + strcat (prefixbuffer, gnupgname); } /* Check whether the gnupg sub directory (or the specified diretory) @@ -1303,11 +1458,8 @@ gnupg_sysconfdir (void) if (!name) { - const char *s1, *s2; - s1 = w32_commondir (); - s2 = DIRSEP_S "etc" DIRSEP_S "gnupg"; - name = xmalloc (strlen (s1) + strlen (s2) + 1); - strcpy (stpcpy (name, s1), s2); + name = xstrconcat (w32_commondir (), DIRSEP_S, "etc", DIRSEP_S, + my_gnupg_dirname (), NULL); gpgrt_annotate_leaked_object (name); } return name; diff --git a/common/init.c b/common/init.c index 62a48f8c7..8ea51c8b0 100644 --- a/common/init.c +++ b/common/init.c @@ -37,6 +37,7 @@ # include <winsock2.h> # endif # include <windows.h> +# include <wctype.h> #endif #include <gcrypt.h> diff --git a/common/iobuf.c b/common/iobuf.c index 748e6935d..e46eeac95 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -166,7 +166,8 @@ block_filter_ctx_t; /* Local prototypes. */ static int underflow (iobuf_t a, int clear_pending_eof); static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target); -static int translate_file_handle (int fd, int for_write); +static iobuf_t do_iobuf_fdopen (gnupg_fd_t fp, const char *mode, int keep_open); + /* Sends any pending data to the filter's FILTER function. Note: this works on the filter and not on the whole pipeline. That is, @@ -389,7 +390,7 @@ fd_cache_close (const char *fname, gnupg_fd_t fp) close (fp); #endif if (DBG_IOBUF) - log_debug ("fd_cache_close (%d) real\n", (int)fp); + log_debug ("fd_cache_close (%d) real\n", FD_DBG (fp)); return; } /* try to reuse a slot */ @@ -696,7 +697,7 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT) { if (DBG_IOBUF) - log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f)); + log_debug ("%s: close fd/handle %d\n", a->fname, FD_DBG (f)); if (!a->keep_open) fd_cache_close (a->no_cache ? NULL : a->fname, f); } @@ -1410,7 +1411,7 @@ iobuf_is_pipe_filename (const char *fname) { if (!fname || (*fname=='-' && !fname[1]) ) return 1; - return check_special_filename (fname, 0, 1) != -1; + return gnupg_check_special_filename (fname) != GNUPG_INVALID_FD; } @@ -1423,7 +1424,7 @@ do_open (const char *fname, int special_filenames, file_filter_ctx_t *fcx; size_t len = 0; int print_only = 0; - int fd; + gnupg_fd_t fd; byte desc[MAX_IOBUF_DESC]; log_assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); @@ -1447,9 +1448,8 @@ do_open (const char *fname, int special_filenames, else if (!fname) return NULL; else if (special_filenames - && (fd = check_special_filename (fname, 0, 1)) != -1) - return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1), - opentype); + && (fd = gnupg_check_special_filename (fname)) != GNUPG_INVALID_FD) + return do_iobuf_fdopen (fd, opentype, 0); else { if (use == IOBUF_INPUT) @@ -1472,7 +1472,8 @@ do_open (const char *fname, int special_filenames, file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); if (DBG_IOBUF) log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n", - a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp)); + a->no, a->subno, fname, iobuf_desc (a, desc), + FD_DBG (fcx->fp)); return a; } @@ -1497,22 +1498,19 @@ iobuf_openrw (const char *fname) static iobuf_t -do_iobuf_fdopen (int fd, const char *mode, int keep_open) +do_iobuf_fdopen (gnupg_fd_t fp, const char *mode, int keep_open) { iobuf_t a; - gnupg_fd_t fp; file_filter_ctx_t *fcx; size_t len = 0; - fp = INT2FD (fd); - a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, iobuf_buffer_size); fcx = xmalloc (sizeof *fcx + 20); fcx->fp = fp; fcx->print_only_name = 1; fcx->keep_open = keep_open; - sprintf (fcx->fname, "[fd %d]", fd); + sprintf (fcx->fname, "[fd %d]", FD_DBG (fp)); a->filter = file_filter; a->filter_ov = fcx; file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); @@ -1525,15 +1523,15 @@ do_iobuf_fdopen (int fd, const char *mode, int keep_open) iobuf_t -iobuf_fdopen (int fd, const char *mode) +iobuf_fdopen (gnupg_fd_t fp, const char *mode) { - return do_iobuf_fdopen (fd, mode, 0); + return do_iobuf_fdopen (fp, mode, 0); } iobuf_t -iobuf_fdopen_nc (int fd, const char *mode) +iobuf_fdopen_nc (gnupg_fd_t fp, const char *mode) { - return do_iobuf_fdopen (fd, mode, 1); + return do_iobuf_fdopen (fp, mode, 1); } @@ -1585,7 +1583,7 @@ iobuf_sockopen (int fd, const char *mode) log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname); iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); #else - a = iobuf_fdopen (fd, mode); + a = do_iobuf_fdopen (fd, mode, 0); #endif return a; } @@ -2644,20 +2642,20 @@ iobuf_get_filelength (iobuf_t a) } -int +gnupg_fd_t iobuf_get_fd (iobuf_t a) { for (; a->chain; a = a->chain) ; if (a->filter != file_filter) - return -1; + return GNUPG_INVALID_FD; { file_filter_ctx_t *b = a->filter_ov; gnupg_fd_t fp = b->fp; - return FD2INT (fp); + return fp; } } @@ -2948,36 +2946,6 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, return nbytes; } -static int -translate_file_handle (int fd, int for_write) -{ -#if defined(HAVE_W32_SYSTEM) - { - int x; - - (void)for_write; - - if (fd == 0) - x = (int) GetStdHandle (STD_INPUT_HANDLE); - else if (fd == 1) - x = (int) GetStdHandle (STD_OUTPUT_HANDLE); - else if (fd == 2) - x = (int) GetStdHandle (STD_ERROR_HANDLE); - else - x = fd; - - if (x == -1) - log_debug ("GetStdHandle(%d) failed: ec=%d\n", - fd, (int) GetLastError ()); - - fd = x; - } -#else - (void)for_write; -#endif - return fd; -} - void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) diff --git a/common/iobuf.h b/common/iobuf.h index 04e6b4421..4354c718d 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -333,11 +333,11 @@ iobuf_t iobuf_openrw (const char *fname); creates an input filter. Note: MODE must reflect the file descriptors actual mode! When the filter is destroyed, the file descriptor is closed. */ -iobuf_t iobuf_fdopen (int fd, const char *mode); +iobuf_t iobuf_fdopen (gnupg_fd_t fd, const char *mode); /* Like iobuf_fdopen, but doesn't close the file descriptor when the filter is destroyed. */ -iobuf_t iobuf_fdopen_nc (int fd, const char *mode); +iobuf_t iobuf_fdopen_nc (gnupg_fd_t fd, const char *mode); /* Create a filter using an existing estream. If MODE contains the letter 'w', creates an output filter. Otherwise, creates an input @@ -590,7 +590,7 @@ uint64_t iobuf_get_filelength (iobuf_t a); /* Return the file descriptor designating the underlying file. This only works with file_filter based pipelines. */ -int iobuf_get_fd (iobuf_t a); +gnupg_fd_t iobuf_get_fd (iobuf_t a); /* Return the real filename, if available. This only supports pipelines that end in file filters. Returns NULL if not diff --git a/common/kem.c b/common/kem.c new file mode 100644 index 000000000..0e498d37e --- /dev/null +++ b/common/kem.c @@ -0,0 +1,220 @@ +/* kem.c - KEM helper functions + * Copyright (C) 2024 g10 Code GmbH. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <gpg-error.h> +#include <gcrypt.h> +#include "mischelp.h" + + +/* domSeperation as per *PGP specs. */ +#define KMAC_KEY "OpenPGPCompositeKeyDerivationFunction" + +/* customizationString as per *PGP specs. */ +#define KMAC_CUSTOM "KDF" + +/* The blocksize used for Keccak by compute_kmac256. */ +#define KECCAK512_BLOCKSIZE 136 + + + +static gpg_error_t +compute_kmac256 (void *digest, size_t digestlen, + const void *key, size_t keylen, + const void *custom, size_t customlen, + gcry_buffer_t *data_iov, int data_iovlen) +{ + gpg_error_t err; + gcry_buffer_t iov[20]; + const unsigned char headPAD[2] = { 1, KECCAK512_BLOCKSIZE }; + unsigned char headK[3]; + const unsigned char pad[KECCAK512_BLOCKSIZE] = { 0 }; + unsigned char right_encode_L[3]; + unsigned int len; + int iovcnt; + + if (data_iovlen >= DIM(iov) - 6) + return gpg_error (GPG_ERR_TOO_LARGE); + + /* Check the validity conditions of NIST SP 800-185 */ + if (keylen >= 255 || customlen >= 255 || digestlen >= 255) + return gpg_error (GPG_ERR_TOO_LARGE); + + iovcnt = 0; + iov[iovcnt].data = "KMAC"; + iov[iovcnt].off = 0; + iov[iovcnt].len = 4; + iovcnt++; + + iov[iovcnt].data = (void *)custom; + iov[iovcnt].off = 0; + iov[iovcnt].len = customlen; + iovcnt++; + + iov[iovcnt].data = (void *)headPAD; + iov[iovcnt].off = 0; + iov[iovcnt].len = sizeof (headPAD); + iovcnt++; + + if (keylen < 32) + { + headK[0] = 1; + headK[1] = (keylen*8)&0xff; + iov[iovcnt].data = headK; + iov[iovcnt].off = 0; + iov[iovcnt].len = 2; + } + else + { + headK[0] = 2; + headK[1] = (keylen*8)>>8; + headK[2] = (keylen*8)&0xff; + iov[iovcnt].data = headK; + iov[iovcnt].off = 0; + iov[iovcnt].len = 3; + } + iovcnt++; + + iov[iovcnt].data = (void *)key; + iov[iovcnt].off = 0; + iov[iovcnt].len = keylen; + iovcnt++; + + len = iov[2].len + iov[3].len + iov[4].len; + len %= KECCAK512_BLOCKSIZE; + + iov[iovcnt].data = (unsigned char *)pad; + iov[iovcnt].off = 0; + iov[iovcnt].len = sizeof (pad) - len; + iovcnt++; + + memcpy (&iov[iovcnt], data_iov, data_iovlen * sizeof (gcry_buffer_t)); + iovcnt += data_iovlen; + + if (digestlen < 32) + { + right_encode_L[0] = (digestlen * 8) & 0xff; + right_encode_L[1] = 1; + } + else + { + right_encode_L[0] = (digestlen * 8) >> 8; + right_encode_L[1] = (digestlen * 8) & 0xff; + right_encode_L[2] = 2; + } + + iov[iovcnt].data = right_encode_L; + iov[iovcnt].off = 0; + iov[iovcnt].len = 3; + iovcnt++; + + err = gcry_md_hash_buffers_ext (GCRY_MD_CSHAKE256, 0, + digest, digestlen, iov, iovcnt); + return err; +} + + +/* Compute KEK (shared secret) for ECC with HASHALGO, ECDH result, + ciphertext in ECC_CT, public key in ECC_PK. */ +gpg_error_t +gnupg_ecc_kem_kdf (void *kek, size_t kek_len, + int hashalgo, const void *ecdh, size_t ecdh_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len) +{ + gcry_buffer_t iov[3]; + unsigned int dlen; + + dlen = gcry_md_get_algo_dlen (hashalgo); + if (kek_len != dlen) + return gpg_error (GPG_ERR_INV_LENGTH); + + memset (iov, 0, sizeof (iov)); + + iov[0].data = (unsigned char *)ecdh; + iov[0].len = ecdh_len; + iov[1].data = (unsigned char *)ecc_ct; + iov[1].len = ecc_ct_len; + iov[2].data = (unsigned char *)ecc_pk; + iov[2].len = ecc_pk_len; + gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3); + + return 0; +} + +/* Compute KEK by combining two KEMs. The caller provides a buffer + * KEK allocated with size KEK_LEN which will receive the computed + * KEK. (ECC_SS, ECC_SS_LEN) is the shared secret of the first key. + * (ECC_CT, ECC_CT_LEN) is the ciphertext of the first key. + * (MLKEM_SS, ECC_SS_LEN) is the shared secret of the second key. + * (MLKEM_CT, MLKEM_CT_LEN) is the ciphertext of the second key. + * (FIXEDINFO, FIXEDINFO_LEN) is an octet string used to bind the KEK + * to a the key; for PGP we use the concatenation of the session key's + * algorithm id and the v5 fingerprint of the key. + */ +gpg_error_t +gnupg_kem_combiner (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *mlkem_ct, size_t mlkem_ct_len, + const void *fixedinfo, size_t fixedinfo_len) +{ + gpg_error_t err; + gcry_buffer_t iov[6]; + + memset (iov, 0, sizeof (iov)); + + iov[0].data = "\x00\x00\x00\x01"; /* Counter */ + iov[0].len = 4; + + iov[1].data = (unsigned char *)ecc_ss; + iov[1].len = ecc_ss_len; + + iov[2].data = (unsigned char *)ecc_ct; + iov[2].len = ecc_ct_len; + + iov[3].data = (unsigned char *)mlkem_ss; + iov[3].len = mlkem_ss_len; + + iov[4].data = (unsigned char *)mlkem_ct; + iov[4].len = mlkem_ct_len; + + iov[5].data = (unsigned char *)fixedinfo; + iov[5].len = fixedinfo_len; + + err = compute_kmac256 (kek, kek_len, + KMAC_KEY, strlen (KMAC_KEY), + KMAC_CUSTOM, strlen (KMAC_CUSTOM), iov, 6); + return err; +} diff --git a/common/miscellaneous.c b/common/miscellaneous.c index 1a090b1f5..a41acc240 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -36,27 +36,6 @@ #include "iobuf.h" #include "i18n.h" -/* Used by libgcrypt for logging. */ -static void -my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) -{ - (void)dummy; - - /* Map the log levels. */ - switch (level) - { - case GCRY_LOG_CONT: level = GPGRT_LOGLVL_CONT; break; - case GCRY_LOG_INFO: level = GPGRT_LOGLVL_INFO; break; - case GCRY_LOG_WARN: level = GPGRT_LOGLVL_WARN; break; - case GCRY_LOG_ERROR:level = GPGRT_LOGLVL_ERROR; break; - case GCRY_LOG_FATAL:level = GPGRT_LOGLVL_FATAL; break; - case GCRY_LOG_BUG: level = GPGRT_LOGLVL_BUG; break; - case GCRY_LOG_DEBUG:level = GPGRT_LOGLVL_DEBUG; break; - default: level = GPGRT_LOGLVL_ERROR; break; - } - log_logv (level, fmt, arg_ptr); -} - /* This function is called by libgcrypt on a fatal error. */ static void @@ -100,7 +79,6 @@ my_gcry_outofcore_handler (void *opaque, size_t req_n, unsigned int flags) void setup_libgcrypt_logging (void) { - gcry_set_log_handler (my_gcry_logger, NULL); gcry_set_fatalerror_handler (my_gcry_fatalerror_handler, NULL); gcry_set_outofcore_handler (my_gcry_outofcore_handler, NULL); } @@ -687,3 +665,53 @@ parse_compatibility_flags (const char *string, unsigned int *flagvar, *flagvar |= result; return 0; } + + +/* Convert STRING consisting of base64 characters into its binary + * representation and store the result in a newly allocated buffer at + * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain + * base64 decoding is done. If it is the empty string the decoder + * will skip everything until a "-----BEGIN " line has been seen, + * decoding then ends at a "----END " line. On failure the function + * returns an error code and sets R_BUFFER to NULL. If the decoded + * data has a length of 0 a dummy buffer will still be allocated and + * the length is set to 0. */ +gpg_error_t +b64decode (const char *string, const char *title, + void **r_buffer, size_t *r_buflen) +{ + gpg_error_t err; + gpgrt_b64state_t state; + size_t nbytes; + char *buffer; + + *r_buffer = NULL; + *r_buflen = 0; + + buffer = xtrystrdup (string); + if (!buffer) + return gpg_error_from_syserror(); + + state = gpgrt_b64dec_start (title); + if (!state) + { + err = gpg_error_from_syserror (); + xfree (buffer); + return err; + } + err = gpgrt_b64dec_proc (state, buffer, strlen (buffer), &nbytes); + if (!err) + { + err = gpgrt_b64dec_finish (state); + state = NULL; + } + if (err) + xfree (buffer); + else + { + *r_buffer = buffer; + *r_buflen = nbytes; + } + gpgrt_b64dec_finish (state); /* Make sure it is released. */ + return err; +} diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 493054950..92f0dfbcd 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -43,23 +43,34 @@ static struct { const char *oidstr; /* IETF formatted OID. */ unsigned int nbits; /* Nominal bit length of the curve. */ const char *alias; /* NULL or alternative name of the curve. */ + const char *abbr; /* NULL or abbreviated name of the curve. */ int pubkey_algo; /* Required OpenPGP algo or 0 for ECDSA/ECDH. */ + enum gcry_kem_algos kem_algo; /* 0 or the KEM algorithm for PQC. */ } oidtable[] = { - { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH }, - { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA }, - { "Curve25519", "1.3.101.110", 255, "cv25519", PUBKEY_ALGO_ECDH }, - { "Ed25519", "1.3.101.112", 255, "ed25519", PUBKEY_ALGO_EDDSA }, - { "X448", "1.3.101.111", 448, "cv448", PUBKEY_ALGO_ECDH }, - { "Ed448", "1.3.101.113", 456, "ed448", PUBKEY_ALGO_EDDSA }, + { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", NULL, + PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X25519 /* only during development */}, + { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", NULL, + PUBKEY_ALGO_EDDSA }, + { "Curve25519", "1.3.101.110", 255, "cv25519", NULL, + PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X25519 }, + { "Ed25519", "1.3.101.112", 255, "ed25519", NULL, + PUBKEY_ALGO_EDDSA }, + { "X448", "1.3.101.111", 448, "cv448", NULL, + PUBKEY_ALGO_ECDH, GCRY_KEM_RAW_X448 }, + { "Ed448", "1.3.101.113", 456, "ed448", NULL, + PUBKEY_ALGO_EDDSA }, { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, { "NIST P-521", "1.3.132.0.35", 521, "nistp521" }, - { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256 }, - { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384 }, - { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512 }, + { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256, NULL, "bp256", + 0, GCRY_KEM_RAW_BP256 }, + { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384, NULL, "bp384", + 0, GCRY_KEM_RAW_BP384 }, + { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512, NULL, "bp512", + 0, GCRY_KEM_RAW_BP512 }, { "secp256k1", "1.3.132.0.10", 256 }, @@ -477,10 +488,20 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo) /* Map an OpenPGP OID to the Libgcrypt curve name. Returns NULL for - * unknown curve names. Unless CANON is set we prefer an alias name - * here which is more suitable for printing. */ + * unknown curve names. MODE defines which version of the curve name + * is returned. For example: + * + * | OID | mode=0 | mode=1 | mode=2 | + * |----------------------+-----------------+-----------------+----------| + * | 1.2.840.10045.3.1.7 | nistp256 | NIST P-256 | nistp256 | + * | 1.3.36.3.3.2.8.1.1.7 | brainpoolP256r1 | brainpoolP256r1 | bp256 | + * + * Thus mode 0 returns the name as commonly used gpg, mode 1 returns + * the canonical name, and mode 2 prefers an abbreviated name over the + * commonly used name. + */ const char * -openpgp_oid_to_curve (const char *oidstr, int canon) +openpgp_oid_to_curve (const char *oidstr, int mode) { int i; @@ -489,7 +510,15 @@ openpgp_oid_to_curve (const char *oidstr, int canon) for (i=0; oidtable[i].name; i++) if (!strcmp (oidtable[i].oidstr, oidstr)) - return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; + { + if (mode == 2) + { + if (oidtable[i].abbr) + return oidtable[i].abbr; + mode = 0; /* No abbreviation - fallback to mode 0. */ + } + return !mode && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; + } return NULL; } @@ -517,6 +546,29 @@ openpgp_oid_or_name_to_curve (const char *oidname, int canon) } +/* Return the KEM algorithm id for the curve with OIDNAME. */ +enum gcry_kem_algos +openpgp_oid_to_kem_algo (const char *oidname) +{ + int i; + + if (!oidname) + return 0; + + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].oidstr, oidname)) + return oidtable[i].kem_algo; + + for (i=0; oidtable[i].name; i++) + if (!ascii_strcasecmp (oidtable[i].name, oidname) + || (oidtable[i].alias + && !ascii_strcasecmp (oidtable[i].alias, oidname))) + return oidtable[i].kem_algo; + + return 0; +} + + /* Return true if the curve with NAME is supported. */ static int curve_supported_p (const char *name) @@ -574,7 +626,9 @@ openpgp_is_curve_supported (const char *name, int *r_algo, { if ((!ascii_strcasecmp (name, oidtable[idx].name) || (oidtable[idx].alias - && !ascii_strcasecmp (name, (oidtable[idx].alias)))) + && !ascii_strcasecmp (name, (oidtable[idx].alias))) + || (oidtable[idx].abbr + && !ascii_strcasecmp (name, (oidtable[idx].abbr)))) && curve_supported_p (oidtable[idx].name)) { if (r_algo) @@ -598,6 +652,7 @@ map_gcry_pk_to_openpgp (enum gcry_pk_algos algo) case GCRY_PK_EDDSA: return PUBKEY_ALGO_EDDSA; case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; + case GCRY_PK_KEM: return PUBKEY_ALGO_KYBER; default: return algo < 110 ? (pubkey_algo_t)algo : 0; } } diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index 180680bcc..bf11d597f 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -171,7 +171,11 @@ typedef enum PUBKEY_ALGO_ECDSA = 19, /* RFC-6637 */ PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */ /* 21 reserved by OpenPGP. */ - PUBKEY_ALGO_EDDSA = 22, /* EdDSA (not yet assigned). */ + PUBKEY_ALGO_EDDSA = 22, /* EdDSA. */ + PUBKEY_ALGO_KYBER = 29, /* Kyber */ + PUBKEY_ALGO_DIL3_25519 = 35, /* Dilithium3 + Ed25519 (aka ML-DSA-65) */ + PUBKEY_ALGO_DIL5_448 = 36, /* Dilithium5 + Ed448 (aka ML-DSA-87) */ + PUBKEY_ALGO_SPHINX_SHA2 = 41, /* SPHINX+-simple-SHA2 (aka SLH-DSA-SHA2) */ PUBKEY_ALGO_PRIVATE10 = 110 } pubkey_algo_t; @@ -206,7 +210,7 @@ compress_algo_t; #define OPENPGP_MAX_NPKEY 5 /* Maximum number of public key parameters. */ #define OPENPGP_MAX_NSKEY 7 /* Maximum number of secret key parameters. */ #define OPENPGP_MAX_NSIG 2 /* Maximum number of signature parameters. */ -#define OPENPGP_MAX_NENC 2 /* Maximum number of encryption parameters. */ +#define OPENPGP_MAX_NENC 4 /* Maximum number of encryption parameters. */ /* Decode an rfc4880 encoded S2K count. */ diff --git a/common/sexputil.c b/common/sexputil.c index c7471be85..15fd7cf1d 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -992,7 +992,7 @@ get_pk_algo_from_key (gcry_sexp_t key) gcry_sexp_t list; const char *s; size_t n; - char algoname[6]; + char algoname[10]; int algo = 0; list = gcry_sexp_nth (key, 1); @@ -1194,3 +1194,47 @@ cipher_mode_to_string (int mode) default: return "[?]"; } } + +/* Return the cannonical name of the ECC curve in KEY. */ +const char * +get_ecc_curve_from_key (gcry_sexp_t key) +{ + gcry_sexp_t list = NULL; + gcry_sexp_t l2 = NULL; + const char *curve_name = NULL; + char *name = NULL; + + /* Check that the first element is valid. */ + list = gcry_sexp_find_token (key, "public-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "protected-private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "shadowed-private-key", 0); + if (!list) + goto leave; + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + l2 = NULL; + + name = gcry_sexp_nth_string (list, 0); + if (!name) + goto leave; + + if (gcry_pk_map_name (name) != GCRY_PK_ECC) + goto leave; + + l2 = gcry_sexp_find_token (list, "curve", 0); + xfree (name); + name = gcry_sexp_nth_string (l2, 1); + curve_name = openpgp_oid_or_name_to_curve (name, 1); + gcry_sexp_release (l2); + + leave: + xfree (name); + gcry_sexp_release (list); + return curve_name; +} diff --git a/common/ssh-utils.c b/common/ssh-utils.c index ab29558cf..d27e2e200 100644 --- a/common/ssh-utils.c +++ b/common/ssh-utils.c @@ -259,7 +259,7 @@ get_fingerprint (gcry_sexp_t key, int algo, } else { - struct b64state b64s; + gpgrt_b64state_t b64s; estream_t stream; char *p; long int len; @@ -273,15 +273,15 @@ get_fingerprint (gcry_sexp_t key, int algo, goto leave; } - err = b64enc_start_es (&b64s, stream, ""); - if (err) + b64s = gpgrt_b64enc_start (stream, ""); + if (!b64s) { es_fclose (stream); goto leave; } - err = b64enc_write (&b64s, - gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo)); + err = gpgrt_b64enc_write (b64s, gcry_md_read (md, algo), + gcry_md_get_algo_dlen (algo)); if (err) { es_fclose (stream); @@ -289,7 +289,7 @@ get_fingerprint (gcry_sexp_t key, int algo, } /* Finish, get the length, and close the stream. */ - err = b64enc_finish (&b64s); + err = gpgrt_b64enc_finish (b64s); len = es_ftell (stream); es_fclose (stream); if (err) @@ -566,7 +566,7 @@ ssh_public_key_in_base64 (gcry_sexp_t key, estream_t stream, const char *identifier = NULL; void *blob = NULL; size_t bloblen; - struct b64state b64_state; + gpgrt_b64state_t b64_state; algo = get_pk_algo_from_key (key); if (algo == 0) @@ -624,15 +624,15 @@ ssh_public_key_in_base64 (gcry_sexp_t key, estream_t stream, es_fprintf (stream, "%s ", identifier); - err = b64enc_start_es (&b64_state, stream, ""); - if (err) + b64_state = gpgrt_b64enc_start (stream, ""); + if (!b64_state) { es_free (blob); - return err; + return gpg_error_from_syserror (); } - err = b64enc_write (&b64_state, blob, bloblen); - b64enc_finish (&b64_state); + err = gpgrt_b64enc_write (b64_state, blob, bloblen); + gpgrt_b64enc_finish (b64_state); es_free (blob); if (err) return err; diff --git a/common/sysutils.c b/common/sysutils.c index 90627b7c8..780af58bd 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -113,6 +113,8 @@ static int allow_special_filenames; #ifdef HAVE_W32_SYSTEM /* State of gnupg_inhibit_set_foregound_window. */ static int inhibit_set_foregound_window; +/* Disable the use of _open_osfhandle. */ +static int no_translate_sys2libc_fd; #endif @@ -351,6 +353,16 @@ enable_special_filenames (void) } +/* Disable the use use of _open_osfhandle on Windows. */ +void +disable_translate_sys2libc_fd (void) +{ +#ifdef HAVE_W32_SYSTEM + no_translate_sys2libc_fd = 1; +#endif +} + + /* Return a string which is used as a kind of process ID. */ const byte * get_session_marker (size_t *rlen) @@ -537,10 +549,10 @@ gnupg_usleep (unsigned int usecs) different from the libc file descriptors (like open). This function translates system file handles to libc file handles. FOR_WRITE gives the direction of the handle. */ -int +#if defined(HAVE_W32_SYSTEM) +static int translate_sys2libc_fd (gnupg_fd_t fd, int for_write) { -#if defined(HAVE_W32_SYSTEM) int x; if (fd == GNUPG_INVALID_FD) @@ -552,24 +564,20 @@ translate_sys2libc_fd (gnupg_fd_t fd, int for_write) if (x == -1) log_error ("failed to translate osfhandle %p\n", (void *) fd); return x; -#else /*!HAVE_W32_SYSTEM */ - (void)for_write; - return fd; -#endif } +#endif /*!HAVE_W32_SYSTEM */ + /* This is the same as translate_sys2libc_fd but takes an integer - which is assumed to be such an system handle. On WindowsCE the - passed FD is a rendezvous ID and the function finishes the pipe - creation. */ + which is assumed to be such an system handle. */ int translate_sys2libc_fd_int (int fd, int for_write) { #ifdef HAVE_W32_SYSTEM - if (fd <= 2) - return fd; /* Do not do this for error, stdin, stdout, stderr. */ + if (fd <= 2 || no_translate_sys2libc_fd) + return fd; /* Do not do this for stdin, stdout, and stderr. */ - return translate_sys2libc_fd ((void*)fd, for_write); + return translate_sys2libc_fd ((void*)(intptr_t)fd, for_write); #else (void)for_write; return fd; @@ -577,6 +585,70 @@ translate_sys2libc_fd_int (int fd, int for_write) } +/* + * Parse the string representation of a file reference (file handle on + * Windows or file descriptor on POSIX) in FDSTR. The string + * representation may be either of folllowing: + + * (1) 0, 1, or 2 which means stdin, stdout, and stderr, respectively. + * (2) Integer representation (by %d of printf). + * (3) Hex representation which starts as "0x". + * + * Then, fill R_SYSHD, according to the value of a file reference. + * + */ +gpg_error_t +gnupg_parse_fdstr (const char *fdstr, es_syshd_t *r_syshd) +{ + int fd = -1; +#ifdef HAVE_W32_SYSTEM + gnupg_fd_t hd; + char *endptr; + int base; + + if (!strcmp (fdstr, "0")) + fd = 0; + else if (!strcmp (fdstr, "1")) + fd = 1; + else if (!strcmp (fdstr, "2")) + fd = 2; + + if (fd >= 0) + { + r_syshd->type = ES_SYSHD_FD; + r_syshd->u.fd = fd; + return 0; + } + + if (!strncmp (fdstr, "0x", 2)) + { + base = 16; + fdstr += 2; + } + else + base = 10; + + gpg_err_set_errno (0); +#ifdef _WIN64 + hd = (gnupg_fd_t)strtoll (fdstr, &endptr, base); +#else + hd = (gnupg_fd_t)strtol (fdstr, &endptr, base); +#endif + if (errno != 0 || endptr == fdstr || *endptr != '\0') + return gpg_error (GPG_ERR_INV_ARG); + + r_syshd->type = ES_SYSHD_HANDLE; + r_syshd->u.handle = hd; + return 0; +#else + fd = atoi (fdstr); + r_syshd->type = ES_SYSHD_FD; + r_syshd->u.fd = fd; + return 0; +#endif +} + + /* Check whether FNAME has the form "-&nnnn", where N is a non-zero * number. Returns this number or -1 if it is not the case. If the * caller wants to use the file descriptor for writing FOR_WRITE shall @@ -594,13 +666,74 @@ check_special_filename (const char *fname, int for_write, int notranslate) for (i=0; digitp (fname+i); i++ ) ; if (!fname[i]) - return notranslate? atoi (fname) - /**/ : translate_sys2libc_fd_int (atoi (fname), for_write); + { + if (notranslate) + return atoi (fname); + else + { + es_syshd_t syshd; + + if (gnupg_parse_fdstr (fname, &syshd)) + return -1; + +#ifdef HAVE_W32_SYSTEM + if (syshd.type == ES_SYSHD_FD) + return syshd.u.fd; + else + return translate_sys2libc_fd ((gnupg_fd_t)syshd.u.handle, for_write); +#else + (void)for_write; + return syshd.u.fd; +#endif + } + } } return -1; } +/* Check whether FNAME has the form "-&nnnn", where N is a number + * representing a file. Returns GNUPG_INVALID_FD if it is not the + * case. Returns a file descriptor on POSIX, a system handle on + * Windows. */ +gnupg_fd_t +gnupg_check_special_filename (const char *fname) +{ + if (allow_special_filenames + && fname && *fname == '-' && fname[1] == '&') + { + int i; + + fname += 2; + for (i=0; digitp (fname+i); i++ ) + ; + if (!fname[i]) + { + es_syshd_t syshd; + + if (gnupg_parse_fdstr (fname, &syshd)) + return GNUPG_INVALID_FD; + +#ifdef HAVE_W32_SYSTEM + if (syshd.type == ES_SYSHD_FD) + { + if (syshd.u.fd == 0) + return GetStdHandle (STD_INPUT_HANDLE); + else if (syshd.u.fd == 1) + return GetStdHandle (STD_OUTPUT_HANDLE); + else if (syshd.u.fd == 2) + return GetStdHandle (STD_ERROR_HANDLE); + } + else + return syshd.u.handle; +#else + return syshd.u.fd; +#endif + } + } + return GNUPG_INVALID_FD; +} + /* Replacement for tmpfile(). This is required because the tmpfile function of Windows' runtime library is broken, insecure, ignores TMPDIR and so on. In addition we create a file with an inheritable @@ -1158,6 +1291,19 @@ gnupg_setenv (const char *name, const char *value, int overwrite) return setenv (name, value, overwrite); #else /*!HAVE_SETENV*/ if (! getenv (name) || overwrite) +#if defined(HAVE_W32_SYSTEM) && defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) + { + int e = _putenv_s (name, value); + + if (e) + { + gpg_err_set_errno (e); + return -1; + } + else + return 0; + } +#else { char *buf; @@ -1175,6 +1321,7 @@ gnupg_setenv (const char *name, const char *value, int overwrite) # endif return putenv (buf); } +#endif /*!HAVE_W32_SYSTEM*/ return 0; #endif /*!HAVE_SETENV*/ } @@ -1199,6 +1346,18 @@ gnupg_unsetenv (const char *name) #ifdef HAVE_UNSETENV return unsetenv (name); +#elif defined(HAVE_W32_SYSTEM) && defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) + { + int e = _putenv_s (name, ""); + + if (e) + { + gpg_err_set_errno (e); + return -1; + } + else + return 0; + } #else /*!HAVE_UNSETENV*/ { char *buf; @@ -1829,3 +1988,22 @@ gnupg_fd_valid (int fd) close (d); return 1; } + + +/* Open a stream from FD (a file descriptor on POSIX, a system + handle on Windows), non-closed. */ +estream_t +open_stream_nc (gnupg_fd_t fd, const char *mode) +{ + es_syshd_t syshd; + +#ifdef HAVE_W32_SYSTEM + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = fd; +#else + syshd.type = ES_SYSHD_FD; + syshd.u.fd = fd; +#endif + + return es_sysopen_nc (&syshd, mode); +} diff --git a/common/sysutils.h b/common/sysutils.h index a78a81c64..9a90d1018 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -38,12 +38,20 @@ typedef void *gnupg_fd_t; #define GNUPG_INVALID_FD ((void*)(-1)) #define INT2FD(s) ((void *)(s)) -#define FD2INT(h) ((unsigned int)(h)) +# ifdef _WIN64 +# define FD2INT(h) ((intptr_t)(h)) +# else +# define FD2INT(h) ((unsigned int)(h)) +# endif +#define FD_DBG(h) ((int)(intptr_t)(h)) +#define FD2NUM(h) ((int)(intptr_t)(h)) #else typedef int gnupg_fd_t; #define GNUPG_INVALID_FD (-1) #define INT2FD(s) (s) #define FD2INT(h) (h) +#define FD_DBG(h) (h) +#define FD2NUM(h) (h) #endif #ifdef HAVE_STAT @@ -67,14 +75,17 @@ void trap_unaligned (void); int disable_core_dumps (void); int enable_core_dumps (void); void enable_special_filenames (void); +void disable_translate_sys2libc_fd (void); + const unsigned char *get_session_marker (size_t *rlen); unsigned int get_uint_nonce (void); /*int check_permissions (const char *path,int extension,int checkonly);*/ void gnupg_sleep (unsigned int seconds); void gnupg_usleep (unsigned int usecs); -int translate_sys2libc_fd (gnupg_fd_t fd, int for_write); int translate_sys2libc_fd_int (int fd, int for_write); +gpg_error_t gnupg_parse_fdstr (const char *fdstr, es_syshd_t *r_syshd); int check_special_filename (const char *fname, int for_write, int notranslate); +gnupg_fd_t gnupg_check_special_filename (const char *fname); FILE *gnupg_tmpfile (void); void gnupg_reopen_std (const char *pgmname); void gnupg_inhibit_set_foregound_window (int yes); @@ -108,6 +119,7 @@ gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname); gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); int gnupg_inotify_has_name (int fd, const char *name); +estream_t open_stream_nc (gnupg_fd_t fd, const char *mode); #ifdef HAVE_W32_SYSTEM int gnupg_w32_set_errno (int ec); diff --git a/common/t-b64.c b/common/t-b64.c deleted file mode 100644 index 16c079d1d..000000000 --- a/common/t-b64.c +++ /dev/null @@ -1,283 +0,0 @@ -/* t-b64.c - Module tests for b64enc.c and b64dec.c - * Copyright (C) 2008 Free Software Foundation, Inc. - * Copyright (C) 2008, 2023 g10 Code GmbH - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include <config.h> -#include <stdio.h> -#include <stdlib.h> - -#include "util.h" - -#define pass() do { ; } while(0) -#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ - __FILE__,__LINE__, (a)); \ - errcount++; \ - } while(0) -#define oops() do { fprintf (stderr, "%s:%d: ooops\n", \ - __FILE__,__LINE__); \ - exit (2); \ - } while(0) - -static int verbose; -static int errcount; - - -/* Convert STRING consisting of hex characters into its binary - * representation and return it as an allocated buffer. The valid - * length of the buffer is returned at R_LENGTH. The string is - * delimited by end of string. The function returns NULL on - * error. */ -static void * -hex2buffer (const char *string, size_t *r_length) -{ - const char *s; - unsigned char *buffer; - size_t length; - - buffer = xmalloc (strlen(string)/2+1); - length = 0; - for (s=string; *s; s +=2 ) - { - if (!hexdigitp (s) || !hexdigitp (s+1)) - return NULL; /* Invalid hex digits. */ - ((unsigned char*)buffer)[length++] = xtoi_2 (s); - } - *r_length = length; - return buffer; -} - - -static void -test_b64decode (void) -{ - static struct { - const char *string; /* String to test. */ - const char *title; /* title parameter. */ - gpg_error_t err; /* expected error. */ - const char *datastr; /* Expected data (hex encoded) */ - } tests[] = { - { "YQ==", NULL, 0, - "61" }, - { "YWE==", NULL, 0, - "6161" }, - { "YWFh", NULL, 0, - "616161" }, - { "YWFhYQ==", NULL, 0, - "61616161" }, - { "YWJjZA==", NULL, 0, - "61626364" }, - { "AA=", NULL, 0, - "00" }, - { "AAEA=", NULL, 0, - "000100" }, - { "/w==", NULL, 0, - "ff" }, - { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", NULL, 0, - "a1143012a0030a0103a10b06092a864882f712010202" }, - { "oRQwEqADCgEDoQsGCSqGSIL3EgECA-==", NULL, GPG_ERR_BAD_DATA, - "a1143012a0030a0103a10b06092a864882f712010202" }, - { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", "", 0, - "" }, - { "-----BEGIN PGP\n\n" - "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==\n" - "-----END PGP\n", "", 0, - "a1143012a0030a0103a10b06092a864882f712010202" }, - - { "", NULL, 0, - "" } - }; - int tidx; - gpg_error_t err; - void *data = NULL; - size_t datalen; - char *wantdata = NULL; - size_t wantdatalen; - - for (tidx = 0; tidx < DIM(tests); tidx++) - { - xfree (wantdata); - if (!(wantdata = hex2buffer (tests[tidx].datastr, &wantdatalen))) - oops (); - xfree (data); - err = b64decode (tests[tidx].string, tests[tidx].title, &data, &datalen); - if (verbose) - fprintf (stderr, "%s:%d: test %d, err=%d, datalen=%zu\n", - __FILE__, __LINE__, tidx, err, datalen); - if (gpg_err_code (err) != tests[tidx].err) - fail (tidx); - else if (err) - pass (); - else if (wantdatalen != datalen) - fail (tidx); - else if (memcmp (wantdata, data, datalen)) - fail (tidx); - else - pass (); - } - xfree (wantdata); - xfree (data); -} - - -static void -test_b64enc_pgp (const char *string) -{ - gpg_error_t err; - struct b64state state; - - if (!string) - string = "a"; - - err = b64enc_start (&state, stdout, "PGP MESSAGE"); - if (err) - fail (1); - - err = b64enc_write (&state, string, strlen (string)); - if (err) - fail (2); - - err = b64enc_finish (&state); - if (err) - fail (3); - - pass (); -} - - -static void -test_b64enc_file (const char *fname) -{ - gpg_error_t err; - struct b64state state; - FILE *fp; - char buffer[50]; - size_t nread; - - fp = fname ? fopen (fname, "r") : stdin; - if (!fp) - { - fprintf (stderr, "%s:%d: can't open '%s': %s\n", - __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); - fail (0); - } - - err = b64enc_start (&state, stdout, "DATA"); - if (err) - fail (1); - - while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) - { - err = b64enc_write (&state, buffer, nread); - if (err) - fail (2); - } - - err = b64enc_finish (&state); - if (err) - fail (3); - - fclose (fp); - pass (); -} - - -static void -test_b64dec_file (const char *fname) -{ - gpg_error_t err; - struct b64state state; - FILE *fp; - char buffer[50]; - size_t nread, nbytes; - - fp = fname ? fopen (fname, "r") : stdin; - if (!fp) - { - fprintf (stderr, "%s:%d: can't open '%s': %s\n", - __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); - fail (0); - } - - err = b64dec_start (&state, ""); - if (err) - fail (1); - - while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) - { - err = b64dec_proc (&state, buffer, nread, &nbytes); - if (err) - { - if (gpg_err_code (err) == GPG_ERR_EOF) - break; - fail (2); - } - else if (nbytes) - fwrite (buffer, 1, nbytes, stdout); - } - - err = b64dec_finish (&state); - if (err) - fail (3); - - fclose (fp); - pass (); -} - - - -int -main (int argc, char **argv) -{ - int do_encode = 0; - int do_decode = 0; - int do_pgpdecode = 0; - - if (argc) - { argc--; argv++; } - if (argc && !strcmp (argv[0], "--verbose")) - { - verbose = 1; - argc--; argv++; - } - - if (argc && !strcmp (argv[0], "--encode")) - { - do_encode = 1; - argc--; argv++; - } - else if (argc && !strcmp (argv[0], "--decode")) - { - do_decode = 1; - argc--; argv++; - } - else if (argc) - do_pgpdecode = 1; - - if (do_encode) - test_b64enc_file (argc? *argv: NULL); - else if (do_decode) - test_b64dec_file (argc? *argv: NULL); - else if (do_pgpdecode) - test_b64enc_pgp (argc? *argv: NULL); - else - test_b64decode (); - - return !!errcount; -} diff --git a/common/t-exechelp.c b/common/t-exechelp.c index 3bf082bbb..2179ef2a0 100644 --- a/common/t-exechelp.c +++ b/common/t-exechelp.c @@ -29,7 +29,7 @@ static int verbose; - +#ifndef HAVE_W32_SYSTEM static void print_open_fds (int *array) { @@ -169,20 +169,168 @@ test_close_all_fds (void) } } +#endif + +static char buff12k[1024*12]; +static char buff4k[1024*4]; + +static void +run_server (void) +{ + estream_t fp; + int i; + char *p; + unsigned int len; + int ret; + es_syshd_t syshd; + size_t n; + off_t o; + +#ifdef HAVE_W32_SYSTEM + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = (HANDLE)_get_osfhandle (1); +#else + syshd.type = ES_SYSHD_FD; + syshd.u.fd = 1; +#endif + + fp = es_sysopen_nc (&syshd, "w"); + if (fp == NULL) + { + fprintf (stderr, "es_fdopen failed\n"); + exit (1); + } + + /* Fill the buffer by ASCII chars. */ + p = buff12k; + for (i = 0; i < sizeof (buff12k); i++) + if ((i % 64) == 63) + *p++ = '\n'; + else + *p++ = (i % 64) + '@'; + + len = sizeof (buff12k); + + ret = es_write (fp, (void *)&len, sizeof (len), NULL); + if (ret) + { + fprintf (stderr, "es_write (1) failed\n"); + exit (1); + } + + es_fflush (fp); + + o = 0; + n = len; + + while (1) + { + size_t n0, n1; + + n0 = n > 4096 ? 4096 : n; + memcpy (buff4k, buff12k + o, n0); + + ret = es_write (fp, buff4k, n0, &n1); + if (ret || n0 != n1) + { + fprintf (stderr, "es_write (2) failed\n"); + exit (1); + } + + o += n0; + n -= n0; + if (n == 0) + break; + } + + es_fclose (fp); + exit (0); +} + + +static void +test_pipe_stream (const char *pgmname) +{ + gpg_error_t err; + gnupg_process_t proc; + estream_t outfp; + const char *argv[2]; + unsigned int len; + size_t n; + off_t o; + int ret; + + argv[0] = "--server"; + argv[1] = NULL; + + err = gnupg_process_spawn (pgmname, argv, + (GNUPG_PROCESS_STDOUT_PIPE + |GNUPG_PROCESS_STDERR_KEEP), + NULL, NULL, &proc); + if (err) + { + fprintf (stderr, "gnupg_process_spawn failed\n"); + exit (1); + } + + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); + + ret = es_read (outfp, (void *)&len, sizeof (len), NULL); + if (ret) + { + fprintf (stderr, "es_read (1) failed\n"); + exit (1); + } + + o = 0; + while (1) + { + if (es_feof (outfp)) + break; + + ret = es_read (outfp, buff4k, sizeof (buff4k), &n); + if (ret) + { + fprintf (stderr, "es_read (2) failed\n"); + exit (1); + } + + memcpy (buff12k + o, buff4k, n); + o += n; + } + + if (o != sizeof (buff12k)) + { + fprintf (stderr, "received data with wrong length %d\n", (int)o); + exit (1); + } + es_fclose (outfp); + gnupg_process_release (proc); +} int main (int argc, char **argv) { + const char *myname = "no-pgm"; + if (argc) - { argc--; argv++; } + { + myname = argv[0]; + argc--; argv++; + } if (argc && !strcmp (argv[0], "--verbose")) { verbose = 1; argc--; argv++; } + if (argc && !strcmp (argv[0], "--server")) + run_server (); +#ifndef HAVE_W32_SYSTEM test_close_all_fds (); +#endif + test_pipe_stream (myname); return 0; } diff --git a/common/t-iobuf.c b/common/t-iobuf.c index bdeab99a4..aacf27a8b 100644 --- a/common/t-iobuf.c +++ b/common/t-iobuf.c @@ -1,3 +1,36 @@ +/* t-iobuf.c - Simple module test for iobuf.c + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +/* The whole code here does not very fill into our general test frame + * work patter. But let's keep it as it is. */ + #include <config.h> #include <stdio.h> #include <string.h> @@ -7,6 +40,20 @@ #include "iobuf.h" #include "stringhelp.h" + +static void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + { + fprintf (stderr, "t-iobuf: out of core\n"); + abort (); + } + return p; +} + + /* Return every other byte. In particular, reads two bytes, returns the second one. */ static int @@ -86,7 +133,7 @@ static struct content_filter_state * content_filter_new (const char *buffer) { struct content_filter_state *state - = malloc (sizeof (struct content_filter_state)); + = xmalloc (sizeof (struct content_filter_state)); state->pos = 0; state->len = strlen (buffer); @@ -215,8 +262,7 @@ main (int argc, char *argv[]) allocate a buffer that is 5 bytes long, then no reallocation should be required. */ size = 5; - buffer = malloc (size); - assert (buffer); + buffer = xmalloc (size); max_len = 100; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 4); @@ -229,7 +275,7 @@ main (int argc, char *argv[]) requires 6 bytes of storage. We pass a buffer that is 5 bytes large and we allow the buffer to be grown. */ size = 5; - buffer = malloc (size); + buffer = xmalloc (size); max_len = 100; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 5); @@ -243,7 +289,7 @@ main (int argc, char *argv[]) requires 7 bytes of storage. We pass a buffer that is 5 bytes large and we don't allow the buffer to be grown. */ size = 5; - buffer = malloc (size); + buffer = xmalloc (size); max_len = 5; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 4); diff --git a/common/util.h b/common/util.h index 803ab3d5c..f8447aea7 100644 --- a/common/util.h +++ b/common/util.h @@ -149,36 +149,6 @@ ssize_t read_line (FILE *fp, size_t *max_length); -/*-- b64enc.c and b64dec.c --*/ -struct b64state -{ - unsigned int flags; - int idx; - int quad_count; - FILE *fp; - estream_t stream; - char *title; - unsigned char radbuf[4]; - u32 crc; - int stop_seen:1; - int invalid_encoding:1; - gpg_error_t lasterr; -}; - -gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); -gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, - const char *title); -gpg_error_t b64enc_write (struct b64state *state, - const void *buffer, size_t nbytes); -gpg_error_t b64enc_finish (struct b64state *state); - -gpg_error_t b64dec_start (struct b64state *state, const char *title); -gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, - size_t *r_nbytes); -gpg_error_t b64dec_finish (struct b64state *state); -gpg_error_t b64decode (const char *string, const char *title, - void **r_buffer, size_t *r_buflen); - /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); void log_printcanon (const char *text, @@ -226,6 +196,7 @@ char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid); const char *pubkey_algo_to_string (int algo); const char *hash_algo_to_string (int algo); const char *cipher_mode_to_string (int mode); +const char *get_ecc_curve_from_key (gcry_sexp_t key); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); @@ -257,9 +228,10 @@ int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); int openpgp_oid_is_cv25519 (gcry_mpi_t a); int openpgp_oid_is_cv448 (gcry_mpi_t a); int openpgp_oid_is_ed448 (gcry_mpi_t a); +enum gcry_kem_algos openpgp_oid_to_kem_algo (const char *oidname); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo); -const char *openpgp_oid_to_curve (const char *oid, int canon); +const char *openpgp_oid_to_curve (const char *oid, int mode); const char *openpgp_oid_or_name_to_curve (const char *oidname, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, @@ -274,6 +246,7 @@ void gnupg_set_homedir (const char *newdir); void gnupg_maybe_make_homedir (const char *fname, int quiet); const char *gnupg_homedir (void); int gnupg_default_homedir_p (void); +const char *gnupg_registry_dir (void); const char *gnupg_daemon_rootdir (void); const char *gnupg_socketdir (void); const char *gnupg_sysconfdir (void); @@ -328,6 +301,19 @@ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); +/*-- kem.c --*/ +gpg_error_t gnupg_ecc_kem_kdf (void *kek, size_t kek_len, + int hashalgo, const void *ecdh, size_t ecdh_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *ecc_pk, size_t ecc_pk_len); + +gpg_error_t gnupg_kem_combiner (void *kek, size_t kek_len, + const void *ecc_ss, size_t ecc_ss_len, + const void *ecc_ct, size_t ecc_ct_len, + const void *mlkem_ss, size_t mlkem_ss_len, + const void *mlkem_ct, size_t mlkem_ct_len, + const void *fixedinfo, size_t fixedinfo_len); + /*-- miscellaneous.c --*/ /* This function is called at startup to tell libgcrypt to use our own @@ -388,6 +374,10 @@ struct compatibility_flags_s int parse_compatibility_flags (const char *string, unsigned int *flagvar, const struct compatibility_flags_s *flags); +gpg_error_t b64decode (const char *string, const char *title, + void **r_buffer, size_t *r_buflen); + + /*-- Simple replacement functions. */ diff --git a/configure.ac b/configure.ac index db5f89947..1f0142140 100644 --- a/configure.ac +++ b/configure.ac @@ -28,8 +28,8 @@ min_automake_version="1.16.3" # another commit and push so that the git magic is able to work. m4_define([mym4_package],[gnupg]) m4_define([mym4_major], [2]) -m4_define([mym4_minor], [4]) -m4_define([mym4_micro], [6]) +m4_define([mym4_minor], [5]) +m4_define([mym4_micro], [0]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release @@ -53,15 +53,15 @@ AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org]) # When changing the SWDB tag please also adjust the hard coded tags in # build-aux/speedo.mk, build-aux/getswdb.sh, and Makefile.am # As well as the source info for the man pages. -AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg24", [swdb tag for this branch]) +AC_DEFINE_UNQUOTED(GNUPG_SWDB_TAG, "gnupg26", [swdb tag for this branch]) NEED_GPGRT_VERSION=1.46 NEED_LIBGCRYPT_API=1 -NEED_LIBGCRYPT_VERSION=1.9.1 +NEED_LIBGCRYPT_VERSION=1.11.0 -NEED_LIBASSUAN_API=2 -NEED_LIBASSUAN_VERSION=2.5.0 +NEED_LIBASSUAN_API=3 +NEED_LIBASSUAN_VERSION=3.0.0 NEED_KSBA_API=1 NEED_KSBA_VERSION=1.6.3 @@ -1385,6 +1385,8 @@ AC_CHECK_SIZEOF(time_t,,[[ ]]) GNUPG_TIME_T_UNSIGNED +# Check SOCKET type for Windows. +AC_CHECK_TYPES([SOCKET], [], [], [[#include "winsock2.h"]]) if test "$ac_cv_sizeof_unsigned_short" = "0" \ || test "$ac_cv_sizeof_unsigned_int" = "0" \ diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index 6b194f31c..b7b5b3d15 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -100,7 +100,8 @@ static unsigned int any_cert_of_class; #ifdef HAVE_W32_SYSTEM -/* We load some functions dynamically. Provide typedefs for tehse +#include <wincrypt.h> +/* We load some functions dynamically. Provide typedefs for these * functions. */ typedef HCERTSTORE (WINAPI *CERTOPENSYSTEMSTORE) (HCRYPTPROV hProv, LPCSTR szSubsystemProtocol); diff --git a/dirmngr/crlfetch.c b/dirmngr/crlfetch.c index 5b6b648e2..620edf788 100644 --- a/dirmngr/crlfetch.c +++ b/dirmngr/crlfetch.c @@ -39,10 +39,10 @@ 2008) we need a context in the reader callback. */ struct reader_cb_context_s { - estream_t fp; /* The stream used with the ksba reader. */ - int checked:1; /* PEM/binary detection ahs been done. */ - int is_pem:1; /* The file stream is PEM encoded. */ - struct b64state b64state; /* The state used for Base64 decoding. */ + estream_t fp; /* The stream used with the ksba reader. */ + unsigned int checked:1; /* PEM/binary detection ahs been done. */ + unsigned int is_pem:1; /* The file stream is PEM encoded. */ + gpgrt_b64state_t b64state; /* The state used for Base64 decoding. */ }; @@ -126,14 +126,16 @@ my_es_read (void *opaque, char *buffer, size_t nbytes, size_t *nread) else { cb_ctx->is_pem = 1; - b64dec_start (&cb_ctx->b64state, ""); + cb_ctx->b64state = gpgrt_b64dec_start (""); + if (!cb_ctx->b64state) + return gpg_error_from_syserror (); } } if (cb_ctx->is_pem && *nread) { size_t nread2; - if (b64dec_proc (&cb_ctx->b64state, buffer, *nread, &nread2)) + if (gpgrt_b64dec_proc (cb_ctx->b64state, buffer, *nread, &nread2)) { /* EOF from decoder. */ *nread = 0; @@ -581,7 +583,7 @@ crl_close_reader (ksba_reader_t reader) es_fclose (cb_ctx->fp); /* Release the base64 decoder state. */ if (cb_ctx->is_pem) - b64dec_finish (&cb_ctx->b64state); + gpgrt_b64dec_finish (cb_ctx->b64state); /* Release the callback context. */ xfree (cb_ctx); } diff --git a/dirmngr/dirmngr-client.c b/dirmngr/dirmngr-client.c index 3912bf47b..ece4fbcc9 100644 --- a/dirmngr/dirmngr-client.c +++ b/dirmngr/dirmngr-client.c @@ -308,7 +308,7 @@ main (int argc, char **argv ) opt.dirmngr_program ? opt.dirmngr_program : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR), - ! cmd_ping, + cmd_ping? 0 : ASSHELP_FLAG_AUTOSTART, opt.verbose, 0, NULL, NULL); @@ -441,11 +441,11 @@ static gpg_error_t data_cb (void *opaque, const void *buffer, size_t length) { gpg_error_t err; - struct b64state *state = opaque; + gpgrt_b64state_t state = opaque; if (buffer) { - err = b64enc_write (state, buffer, length); + err = gpgrt_b64enc_write (state, buffer, length); if (err) log_error (_("error writing base64 encoding: %s\n"), gpg_strerror (err)); @@ -853,14 +853,14 @@ do_lookup (assuan_context_t ctx, const char *pattern) gpg_error_t err; const unsigned char *s; char *line, *p; - struct b64state state; + gpgrt_b64state_t state; if (opt.verbose) log_info (_("looking up '%s'\n"), pattern); - err = b64enc_start (&state, stdout, NULL); - if (err) - return err; + state = gpgrt_b64enc_start (es_stdout, NULL); + if (!state) + return gpg_error_from_syserror (); line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1); @@ -885,13 +885,13 @@ do_lookup (assuan_context_t ctx, const char *pattern) err = assuan_transact (ctx, line, - data_cb, &state, + data_cb, state, NULL, NULL, status_cb, NULL); if (opt.verbose > 1) log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay"); - err = b64enc_finish (&state); + err = gpgrt_b64enc_finish (state); xfree (line); return err; diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c index f79a0f877..d58a27372 100644 --- a/dirmngr/dirmngr.c +++ b/dirmngr/dirmngr.c @@ -394,6 +394,9 @@ static enum } tor_mode; +/* Flag indicating that we are in supervised mode. */ +static int is_supervised; + /* Counter for the active connections. */ static int active_connections; @@ -450,9 +453,6 @@ static void handle_connections (assuan_fd_t listen_fd); static void gpgconf_versions (void); -/* NPth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - static const char * my_strusage( int level ) { @@ -980,7 +980,6 @@ static void thread_init (void) { npth_init (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* Now with NPth running we can set the logging callback. Our @@ -1335,6 +1334,8 @@ main (int argc, char **argv) if (!opt.quiet) log_info(_("WARNING: \"%s\" is a deprecated option\n"), "--supervised"); + is_supervised = 1; + /* In supervised mode, we expect file descriptor 3 to be an already opened, listening socket. @@ -2233,7 +2234,7 @@ check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce) if (assuan_sock_check_nonce (fd, nonce)) { log_info (_("error reading nonce on fd %d: %s\n"), - FD2INT (fd), strerror (errno)); + FD_DBG (fd), strerror (errno)); assuan_sock_close (fd); return -1; } @@ -2267,7 +2268,7 @@ start_connection_thread (void *arg) active_connections++; if (opt.verbose) - log_info (_("handler for fd %d started\n"), FD2INT (fd)); + log_info (_("handler for fd %d started\n"), FD_DBG (fd)); session_id = ++last_session_id; if (!session_id) @@ -2275,7 +2276,7 @@ start_connection_thread (void *arg) start_command_handler (fd, session_id); if (opt.verbose) - log_info (_("handler for fd %d terminated\n"), FD2INT (fd)); + log_info (_("handler for fd %d terminated\n"), FD_DBG (fd)); active_connections--; workqueue_run_post_session_tasks (session_id); @@ -2378,7 +2379,7 @@ handle_connections (assuan_fd_t listen_fd) to full second. */ FD_ZERO (&fdset); FD_SET (FD2INT (listen_fd), &fdset); - nfd = FD2INT (listen_fd); + nfd = FD2NUM (listen_fd); if (my_inotify_fd != -1) { FD_SET (my_inotify_fd, &fdset); @@ -2395,7 +2396,7 @@ handle_connections (assuan_fd_t listen_fd) /* Shutdown test. */ if (shutdown_pending) { - if (!active_connections) + if (!active_connections || is_supervised) break; /* ready */ /* Do not accept new connections but keep on running the @@ -2479,8 +2480,8 @@ handle_connections (assuan_fd_t listen_fd) gnupg_fd_t fd; plen = sizeof paddr; - fd = INT2FD (npth_accept (FD2INT(listen_fd), - (struct sockaddr *)&paddr, &plen)); + fd = assuan_sock_accept (listen_fd, + (struct sockaddr *)&paddr, &plen); if (fd == GNUPG_INVALID_FD) { log_error ("accept failed: %s\n", strerror (errno)); @@ -2494,7 +2495,7 @@ handle_connections (assuan_fd_t listen_fd) memset (&argval, 0, sizeof argval); argval.afd = fd; snprintf (threadname, sizeof threadname, - "conn fd=%d", FD2INT(fd)); + "conn fd=%d", FD_DBG (fd)); ret = npth_create (&thread, &tattr, start_connection_thread, argval.aptr); diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index 0edbc0442..270717215 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -34,6 +34,7 @@ # define WIN32_LEAN_AND_MEAN # ifdef HAVE_WINSOCK2_H # include <winsock2.h> +# include <ws2tcpip.h> # endif # include <windows.h> # include <iphlpapi.h> diff --git a/dirmngr/http.c b/dirmngr/http.c index e4c719348..6ae9029be 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -441,7 +441,7 @@ _my_socket_new (int lnr, assuan_fd_t fd) so->refcount = 1; if (opt_debug) log_debug ("http.c:%d:socket_new: object %p for fd %d created\n", - lnr, so, (int)so->fd); + lnr, so, FD_DBG (so->fd)); return so; } #define my_socket_new(a) _my_socket_new (__LINE__, (a)) @@ -453,7 +453,7 @@ _my_socket_ref (int lnr, my_socket_t so) so->refcount++; if (opt_debug > 1) log_debug ("http.c:%d:socket_ref: object %p for fd %d refcount now %d\n", - lnr, so, (int)so->fd, so->refcount); + lnr, so, FD_DBG (so->fd), so->refcount); return so; } #define my_socket_ref(a) _my_socket_ref (__LINE__,(a)) @@ -471,7 +471,7 @@ _my_socket_unref (int lnr, my_socket_t so, so->refcount--; if (opt_debug > 1) log_debug ("http.c:%d:socket_unref: object %p for fd %d ref now %d\n", - lnr, so, (int)so->fd, so->refcount); + lnr, so, FD_DBG (so->fd), so->refcount); if (!so->refcount) { @@ -2200,7 +2200,7 @@ run_ntbtls_handshake (http_t hd) /* Until we support send/recv in estream under Windows we need * to use es_fopencookie. */ # ifdef HAVE_W32_SYSTEM - in = es_fopencookie ((void*)(unsigned int)hd->sock->fd, "rb", + in = es_fopencookie (hd->sock->fd, "rb", simple_cookie_functions); # else in = es_fdopen_nc (hd->sock->fd, "rb"); @@ -2212,7 +2212,7 @@ run_ntbtls_handshake (http_t hd) } # ifdef HAVE_W32_SYSTEM - out = es_fopencookie ((void*)(unsigned int)hd->sock->fd, "wb", + out = es_fopencookie (hd->sock->fd, "wb", simple_cookie_functions); # else out = es_fdopen_nc (hd->sock->fd, "wb"); @@ -3571,7 +3571,7 @@ connect_with_timeout (assuan_fd_t sock, tval.tv_sec = timeout / 1000; tval.tv_usec = (timeout % 1000) * 1000; - n = my_select (FD2INT(sock)+1, &rset, &wset, NULL, &tval); + n = my_select (FD2NUM(sock)+1, &rset, &wset, NULL, &tval); if (n < 0) { err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index 002f1a7a5..2e04582a1 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -373,6 +373,8 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers, || !strcmp (uri->parsed_uri->scheme, "ldaps") || !strcmp (uri->parsed_uri->scheme, "ldapi") || uri->parsed_uri->opaque); +#else + (void)newer; #endif if (is_hkp_s || is_http_s || is_ldap) @@ -590,6 +592,13 @@ ks_action_query (ctrl_t ctrl, const char *url, unsigned int ks_get_flags, return err; #else /* !USE_LDAP */ + (void)ctrl; + (void)url; + (void)ks_get_flags; + (void)filter; + (void)attrs; + (void)newer; + (void)outfp; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif } diff --git a/dirmngr/ks-engine-ldap.c b/dirmngr/ks-engine-ldap.c index 749c0de09..688972a89 100644 --- a/dirmngr/ks-engine-ldap.c +++ b/dirmngr/ks-engine-ldap.c @@ -31,6 +31,8 @@ # define WINVER 0x0500 /* Same as in common/sysutils.c */ # endif # include <winsock2.h> +# include <winldap.h> +# include <winber.h> # include <sddl.h> #endif diff --git a/dirmngr/ldap-parse-uri.c b/dirmngr/ldap-parse-uri.c index 573bcc77f..5856c1b4e 100644 --- a/dirmngr/ldap-parse-uri.c +++ b/dirmngr/ldap-parse-uri.c @@ -162,7 +162,7 @@ ldap_parse_uri (parsed_uri_t *purip, const char *uri) if (password) { - puri->query = calloc (sizeof (*puri->query), 1); + puri->query = calloc (1, sizeof (*puri->query)); if (!puri->query) { err = gpg_err_code_from_syserror (); diff --git a/dirmngr/ldap-wrapper.c b/dirmngr/ldap-wrapper.c index 23d514cf9..2ec944c72 100644 --- a/dirmngr/ldap-wrapper.c +++ b/dirmngr/ldap-wrapper.c @@ -87,7 +87,7 @@ struct wrapper_context_s { struct wrapper_context_s *next; - pid_t pid; /* The pid of the wrapper process. */ + gnupg_process_t proc;/* The wrapper process. */ int printable_pid; /* Helper to print diagnostics after the process has * been cleaned up. */ estream_t fp; /* Connected with stdout of the ldap wrapper. */ @@ -170,10 +170,10 @@ read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count) static void destroy_wrapper (struct wrapper_context_s *ctx) { - if (ctx->pid != (pid_t)(-1)) + if (ctx->proc) { - gnupg_kill_process (ctx->pid); - gnupg_release_process (ctx->pid); + gnupg_process_terminate (ctx->proc); + gnupg_process_release (ctx->proc); } ksba_reader_release (ctx->reader); SAFE_CLOSE (ctx->fp); @@ -260,7 +260,7 @@ read_log_data (struct wrapper_context_s *ctx) if (gpg_err_code (err) == GPG_ERR_EAGAIN) return 0; log_error (_("error reading log from ldap wrapper %d: %s\n"), - (int)ctx->pid, gpg_strerror (err)); + ctx->printable_pid, gpg_strerror (err)); } print_log_line (ctx, NULL); /* Flush. */ SAFE_CLOSE (ctx->log_fp); @@ -438,50 +438,44 @@ ldap_reaper_thread (void *dummy) } /* Check whether the process is still running. */ - if (ctx->pid != (pid_t)(-1)) + if (ctx->proc) { - int status; - - err = gnupg_wait_process ("[dirmngr_ldap]", ctx->pid, 0, - &status); + err = gnupg_process_wait (ctx->proc, 0); if (!err) { + int status; + + gnupg_process_ctl (ctx->proc, GNUPG_PROCESS_GET_EXIT_ID, + &status); if (DBG_EXTPROG) - log_info (_("ldap wrapper %d ready"), (int)ctx->pid); + log_info (_("ldap wrapper %d ready"), ctx->printable_pid); ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); + gnupg_process_release (ctx->proc); + ctx->proc = NULL; any_action = 1; - } - else if (gpg_err_code (err) == GPG_ERR_GENERAL) - { + if (status == 10) log_info (_("ldap wrapper %d ready: timeout\n"), - (int)ctx->pid); + ctx->printable_pid); else log_info (_("ldap wrapper %d ready: exitcode=%d\n"), - (int)ctx->pid, status); - ctx->ready = 1; - gnupg_release_process (ctx->pid); - ctx->pid = (pid_t)(-1); - any_action = 1; + ctx->printable_pid, status); } else if (gpg_err_code (err) != GPG_ERR_TIMEOUT) { log_error (_("waiting for ldap wrapper %d failed: %s\n"), - (int)ctx->pid, gpg_strerror (err)); + ctx->printable_pid, gpg_strerror (err)); any_action = 1; } } /* Check whether we should terminate the process. */ - if (ctx->pid != (pid_t)(-1) - && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) + if (ctx->proc && ctx->stamp != (time_t)(-1) && ctx->stamp < exptime) { - gnupg_kill_process (ctx->pid); + gnupg_process_terminate (ctx->proc); ctx->stamp = (time_t)(-1); log_info (_("ldap wrapper %d stalled - killing\n"), - (int)ctx->pid); + ctx->printable_pid); /* We need to close the log stream because the cleanup * loop waits for it. */ SAFE_CLOSE (ctx->log_fp); @@ -496,10 +490,10 @@ ldap_reaper_thread (void *dummy) { log_debug ("ldap worker states:\n"); for (ctx = reaper_list; ctx; ctx = ctx->next) - log_debug (" c=%p pid=%d/%d rdr=%p logfp=%p" + log_debug (" c=%p pid=%d rdr=%p logfp=%p" " ctrl=%p/%d la=%lu rdy=%d\n", ctx, - (int)ctx->pid, (int)ctx->printable_pid, + ctx->printable_pid, ctx->reader, ctx->log_fp, ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0, (unsigned long)ctx->stamp, ctx->ready); @@ -602,9 +596,9 @@ ldap_wrapper_release_context (ksba_reader_t reader) if (ctx->reader == reader) { if (DBG_EXTPROG) - log_debug ("releasing ldap worker c=%p pid=%d/%d rdr=%p" + log_debug ("releasing ldap worker c=%p pid=%d rdr=%p" " ctrl=%p/%d\n", ctx, - (int)ctx->pid, (int)ctx->printable_pid, + ctx->printable_pid, ctx->reader, ctx->ctrl, ctx->ctrl? ctx->ctrl->refcount:0); @@ -639,8 +633,8 @@ ldap_wrapper_connection_cleanup (ctrl_t ctrl) { ctx->ctrl->refcount--; ctx->ctrl = NULL; - if (ctx->pid != (pid_t)(-1)) - gnupg_kill_process (ctx->pid); + if (ctx->proc) + gnupg_process_terminate (ctx->proc); if (ctx->fp_err) log_info ("%s: reading from ldap wrapper %d failed: %s\n", __func__, ctx->printable_pid, gpg_strerror (ctx->fp_err)); @@ -798,7 +792,7 @@ gpg_error_t ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) { gpg_error_t err; - pid_t pid; + gnupg_process_t process; struct wrapper_context_s *ctx; int i; int j; @@ -854,19 +848,22 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) return err; } - err = gnupg_spawn_process (pgmname, arg_list, - NULL, GNUPG_SPAWN_NONBLOCK, - NULL, &outfp, &errfp, &pid); + err = gnupg_process_spawn (pgmname, arg_list, + (GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + NULL, NULL, &process); if (err) { - xfree (arg_list); + xfree (arg_list); xfree (ctx); log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); return err; } + gnupg_process_get_streams (process, GNUPG_PROCESS_STREAM_NONBLOCK, + NULL, &outfp, &errfp); + gnupg_process_ctl (process, GNUPG_PROCESS_GET_PROC_ID, &ctx->printable_pid); - ctx->pid = pid; - ctx->printable_pid = (int) pid; + ctx->proc = process; ctx->fp = outfp; ctx->log_fp = errfp; ctx->ctrl = ctrl; @@ -902,7 +899,7 @@ ldap_wrapper (ctrl_t ctrl, ksba_reader_t *reader, const char *argv[]) if (DBG_EXTPROG) { log_debug ("ldap wrapper %d started (%p, %s)", - (int)ctx->pid, ctx->reader, pgmname); + ctx->printable_pid, ctx->reader, pgmname); for (i=0; arg_list[i]; i++) log_printf (" [%s]", arg_list[i]); log_printf ("\n"); diff --git a/dirmngr/misc.c b/dirmngr/misc.c index 9cedf911c..d1830237d 100644 --- a/dirmngr/misc.c +++ b/dirmngr/misc.c @@ -583,7 +583,7 @@ gpg_error_t armor_data (char **r_string, const void *data, size_t datalen) { gpg_error_t err; - struct b64state b64state; + gpgrt_b64state_t b64state; estream_t fp; long length; char *buffer; @@ -595,9 +595,15 @@ armor_data (char **r_string, const void *data, size_t datalen) if (!fp) return gpg_error_from_syserror (); - if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK")) - || (err=b64enc_write (&b64state, data, datalen)) - || (err = b64enc_finish (&b64state))) + b64state = gpgrt_b64enc_start (fp, "PGP PUBLIC KEY BLOCK"); + if (!b64state) + { + es_fclose (fp); + return gpg_error_from_syserror (); + } + + if ((err = gpgrt_b64enc_write (b64state, data, datalen)) + || (err = gpgrt_b64enc_finish (b64state))) { es_fclose (fp); return err; diff --git a/doc/DETAILS b/doc/DETAILS index 29e39708b..a278d045f 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -167,11 +167,13 @@ described here. The value is quoted like a C string to avoid control characters (the colon is quoted =\x3a=). For a "pub" record this field is - not used on --fixed-list-mode. A UAT record puts the attribute + not used on --fixed-list-mode. A "uat" record puts the attribute subpacket count here, a space, and then the total attribute - subpacket size. In gpgsm the issuer name comes here. The FPR and FP2 - records store the fingerprints here. The fingerprint of a - revocation key is stored here. + subpacket size. In gpgsm the issuer name comes here. The FPR and + FP2 records store the fingerprints here. The fingerprint of a + revocation key is also stored here. A "grp" records puts the + keygrip here; for combined algorithms the keygrips are delimited + by comma. *** Field 11 - Signature class @@ -243,7 +245,8 @@ described here. *** Field 17 - Curve name For pub, sub, sec, ssb, crt, and crs records this field is used - for the ECC curve name. + for the ECC curve name. For combined algorithms the first and the + second algorithm name, delimited by an underscore are put here. *** Field 18 - Compliance flags diff --git a/doc/Makefile.am b/doc/Makefile.am index 390153c76..66a934cef 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -42,7 +42,7 @@ EXTRA_DIST = samplekeys.asc mksamplekeys com-certs.pem \ FAQ gnupg7.texi mkdefsinc.c defsincdate \ opt-homedir.texi see-also-note.texi specify-user-id.texi \ gpgv.texi yat2m.c ChangeLog-2011 whats-new-in-2.1.txt \ - trust-values.texi + trust-values.texi keyformat.txt BUILT_SOURCES = gnupg-module-overview.png gnupg-module-overview.pdf \ gnupg-card-architecture.png gnupg-card-architecture.pdf \ diff --git a/doc/debugging.texi b/doc/debugging.texi index 6639e184b..a389bdf50 100644 --- a/doc/debugging.texi +++ b/doc/debugging.texi @@ -91,7 +91,7 @@ should not occur but sometimes things go wrong), run it using @item How to find the IP address of a keyserver If a round robin URL of is used for a keyserver -(e.g. subkeys.gnupg.org); it is not easy to see what server is actually +(e.g., subkeys.gnupg.org); it is not easy to see what server is actually used. Using the keyserver debug option as in @smallexample @@ -130,7 +130,7 @@ but Dirmngr's OCSP feature has not been enabled using The far most common reason for this is that the environment variable @code{GPG_TTY} has not been set correctly. Make sure that it has been set to a real tty device and not just to @samp{/dev/tty}; -i.e. @samp{GPG_TTY=tty} is plainly wrong; what you want is +i.e., @samp{GPG_TTY=tty} is plainly wrong; what you want is @samp{GPG_TTY=`tty`} --- note the back ticks. Also make sure that this environment variable gets exported, that is you should follow up the setting with an @samp{export GPG_TTY} (assuming a Bourne style diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi index e50406035..420340ee3 100644 --- a/doc/dirmngr.texi +++ b/doc/dirmngr.texi @@ -213,7 +213,7 @@ however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given in -C syntax (e.g. 0x0042) or as a comma separated list of flag names. To +C syntax (e.g., 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @@ -295,7 +295,7 @@ seconds. @opindex connect-quick-timeout Set the timeout for HTTP and generic TCP connection attempts to N seconds. The value set with the quick variant is used when the ---quick option has been given to certain Assuan commands. The quick +@option{--quick} option has been given to certain Assuan commands. The quick value is capped at the value of the regular connect timeout. The default values are 15 and 2 seconds. Note that the timeout values are for each connection attempt; the connection code will attempt to @@ -375,7 +375,7 @@ there for details; here is an example: as given. Replace USERNAME, PASSWORD, and the 'dc' parts according to the instructions received from your LDAP administrator. Note that only simple authentication - (i.e. cleartext passwords) is supported and thus using ldaps is + (i.e., cleartext passwords) is supported and thus using ldaps is strongly suggested (since 2.2.28 "ldaps" defaults to port 389 and uses STARTTLS). On Windows authentication via AD can be requested by adding @code{gpgNtds=1} after the fourth question @@ -468,7 +468,7 @@ Lines starting with a @samp{#} are comments. Note that as usual all strings entered are expected to be UTF-8 encoded. Obviously this will lead to problems if the password has originally been encoded as Latin-1. There is no other solution here than to put such a -password in the binary encoding into the file (i.e. non-ascii characters +password in the binary encoding into the file (i.e., non-ascii characters won't show up readable).@footnote{The @command{gpgconf} tool might be helpful for frontends as it enables editing this configuration file using percent-escaped strings.} @@ -684,7 +684,7 @@ those certificates on startup and when given a SIGHUP. Certificates which are not readable or do not make up a proper X.509 certificate are ignored; see the log file for details. -Applications using dirmngr (e.g. gpgsm) can request these +Applications using dirmngr (e.g., gpgsm) can request these certificates to complete a trust chain in the same way as with the extra-certs directory (see below). @@ -693,7 +693,7 @@ Note that for OCSP responses the certificate specified using the option @item /etc/gnupg/extra-certs This directory may contain extra certificates which are preloaded -into the internal cache on startup. Applications using dirmngr (e.g. gpgsm) +into the internal cache on startup. Applications using dirmngr (e.g., gpgsm) can request cached certificates to complete a trust chain. This is convenient in cases you have a couple intermediate CA certificates or certificates usually used to sign OCSP responses. @@ -752,7 +752,7 @@ certificate exists it is used to access the special keyservers Please note that @command{gpgsm} accepts Root CA certificates for its own purposes only if they are listed in its file @file{trustlist.txt}. -@command{dirmngr} does not make use of this list - except FIXME. +@command{dirmngr} does not make use of this list --- except FIXME. @mansect notes @@ -802,7 +802,7 @@ Enter @code{HELP} at the prompt to see a list of commands and enter @node Dirmngr Signals @section Use of signals -A running @command{dirmngr} may be controlled by signals, i.e. using +A running @command{dirmngr} may be controlled by signals, i.e., using the @command{kill} command to send a signal to the process. Here is a list of supported signals: @@ -1034,7 +1034,7 @@ includes a local certificate store as well as a list of trusted root certificates. @noindent -The return code is 0 for success; i.e. the certificate has not been +The return code is 0 for success; i.e., the certificate has not been revoked or one of the usual error codes from libgpg-error. @node Dirmngr CHECKOCSP @@ -1069,7 +1069,7 @@ of the global option @option{--ignore-ocsp-service-url}. @noindent -The return code is 0 for success; i.e. the certificate has not been +The return code is 0 for success; i.e., the certificate has not been revoked or one of the usual error codes from libgpg-error. @node Dirmngr CACHECERT @@ -1091,7 +1091,7 @@ Thus the caller is expected to return the certificate for the request as a binary blob. @noindent -The return code is 0 for success; i.e. the certificate has not been +The return code is 0 for success; i.e., the certificate has not been successfully cached or one of the usual error codes from libgpg-error. @node Dirmngr VALIDATE @@ -1191,7 +1191,7 @@ as a binary blob. @c does not yet end up in memory. @c * @code{crl_cache_insert} is called with that descriptor to @c actually read the CRL into the cache. See below for a -@c description of this function. If there is any error (e.g. read +@c description of this function. If there is any error (e.g., read @c problem, CRL not correctly signed or verification of signature @c not possible), this descriptor is rejected and we continue @c with the next name. If the CRL has been successfully loaded, @@ -1217,7 +1217,7 @@ as a binary blob. @c a) An authorityKeyIdentifier with an issuer and serialno exits: The @c certificate is retrieved using @code{find_cert_bysn}. If @c the certificate is in the certificate cache, it is directly -@c returned. Then the requester (i.e. the client who requested the +@c returned. Then the requester (i.e., the client who requested the @c CRL check) is asked via the Assuan inquiry ``SENDCERT'' whether @c he can provide this certificate. If this succeed the returned @c certificate gets cached and returned. Note, that dirmngr does not @@ -1296,7 +1296,7 @@ as a binary blob. @c expiration time of all certificates in the chain. @c @c We first check that the certificate may be used for the requested -@c purpose (i.e. OCSP or CRL signing). If this is not the case +@c purpose (i.e., OCSP or CRL signing). If this is not the case @c GPG_ERR_WRONG_KEY_USAGE is returned. @c @c The next step is to find the trust anchor (root certificate) and to @@ -1320,7 +1320,7 @@ as a binary blob. @c Now the issuer's certificate is looked up: If an @c authorityKeyIdentifier is available, this one is used to locate the @c certificate either using issuer and serialnumber or subject DN -@c (i.e. the issuer's DN) and the keyID. The functions +@c (i.e., the issuer's DN) and the keyID. The functions @c @code{find_cert_bysn) and @code{find_cert_bysubject} are used @c respectively. The have already been described above under the @c description of @code{crl_cache_insert}. If no certificate was found @@ -1334,13 +1334,13 @@ as a binary blob. @c actual certificate is checked and in case this fails the error @c #code{GPG_ERR_BAD_CERT_CHAIN} is returned. If the signature checks out, the @c maximum chain length of the issuing certificate is checked as well as -@c the capability of the certificate (i.e. whether he may be used for +@c the capability of the certificate (i.e., whether he may be used for @c certificate signing). Then the certificate is prepended to our list @c representing the certificate chain. Finally the loop is continued now @c with the issuer's certificate as the current certificate. @c @c After the end of the loop and if no error as been encountered -@c (i.e. the certificate chain has been assempled correctly), a check is +@c (i.e., the certificate chain has been assempled correctly), a check is @c done whether any certificate expired or a critical policy has not been @c met. In any of these cases the validation terminates with an @c appropriate error. diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi index 74c4c10af..1d531fb57 100644 --- a/doc/gpg-agent.texi +++ b/doc/gpg-agent.texi @@ -72,7 +72,7 @@ the included Secure Shell Agent you may start the agent using: @c One way of enforcing this split is a per-key or per-session @c passphrase, known only by the owner, which must be supplied to the @c agent to permit the use of the secret key material. Another way is -@c with an out-of-band permission mechanism (e.g. a button or GUI +@c with an out-of-band permission mechanism (e.g@:. a button or GUI @c interface that the owner has access to, but the supplicant does not). @c @c The rationale for this separation is that it allows access to the @@ -111,8 +111,8 @@ Please make sure that a proper pinentry program has been installed under the default filename (which is system dependent) or use the option @option{pinentry-program} to specify the full name of that program. It is often useful to install a symbolic link from the actual used -pinentry (e.g. @file{@value{BINDIR}/pinentry-gtk}) to the expected -one (e.g. @file{@value{BINDIR}/pinentry}). +pinentry (e.g., @file{@value{BINDIR}/pinentry-gtk}) to the expected +one (e.g., @file{@value{BINDIR}/pinentry}). @manpause @noindent @@ -177,8 +177,8 @@ Windows. If in @file{common.conf} the option @option{no-autostart} is set, any start attempts will be ignored. -In --supervised mode, different file descriptors can be provided for -use as different socket types (e.g. ssh, extra) as long as they are +In @option{--supervised} mode, different file descriptors can be provided for +use as different socket types (e.g., ssh, extra) as long as they are identified in the environment variable @code{LISTEN_FDNAMES} (see sd_listen_fds(3) on some Linux distributions for more information on this convention). @@ -259,7 +259,7 @@ however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given -in C syntax (e.g. 0x0042) or as a comma separated list of flag names. +in C syntax (e.g., 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @@ -345,7 +345,7 @@ specify the logging output. @anchor{option --no-allow-mark-trusted} @item --no-allow-mark-trusted @opindex no-allow-mark-trusted -Do not allow clients to mark keys as trusted, i.e. put them into the +Do not allow clients to mark keys as trusted, i.e., put them into the @file{trustlist.txt} file. This makes it harder for users to inadvertently accept Root-CA keys. @@ -716,7 +716,7 @@ The order in which keys are presented to ssh are: Editing the "Use-for-ssh" values can be done with an editor or using @command{gpg-connect-agent} and "KEYATTR" (Remember to append a colon -to the key; i.e. use "Use-for-ssh:"). +to the key; i.e., use "Use-for-ssh:"). @anchor{option --ssh-fingerprint-digest} @@ -724,7 +724,7 @@ to the key; i.e. use "Use-for-ssh:"). @opindex ssh-fingerprint-digest Select the digest algorithm used to compute ssh fingerprints that are -communicated to the user, e.g. in pinentry dialogs. OpenSSH has +communicated to the user, e.g., in pinentry dialogs. OpenSSH has transitioned from using MD5 to the more secure SHA256. @@ -830,7 +830,7 @@ It might even be advisable to change the permissions to read-only so that this file can't be changed inadvertently. As a special feature a line @code{include-default} will include a global -list of trusted certificates (e.g. @file{@value{SYSCONFDIR}/trustlist.txt}). +list of trusted certificates (e.g., @file{@value{SYSCONFDIR}/trustlist.txt}). This global list is also used if the local list is not available; the @ref{option --no-user-trustlist} enforces the use of only this global list. @@ -892,7 +892,7 @@ The keygrip may be prefixed with a @code{!} to disable an entry. The following example lists exactly one key. Note that keys available through a OpenPGP smartcard in the active smartcard reader are -implicitly added to this list; i.e. there is no need to list them. +implicitly added to this list; i.e., there is no need to list them. @cartouche @smallexample @@ -925,7 +925,7 @@ a small helper script is provided to create these files (@pxref{addgnupghome}). @mansect signals @node Agent Signals @section Use of some signals -A running @command{gpg-agent} may be controlled by signals, i.e. using +A running @command{gpg-agent} may be controlled by signals, i.e., using the @command{kill} command to send a signal to the process. Here is a list of supported signals: @@ -1407,7 +1407,7 @@ convention either the hexified fingerprint of the key shall be used for calling application and a colon: Like @code{gpg:somestring}. @var{error_message} is either a single @code{X} for no error message or -a string to be shown as an error message like (e.g. "invalid +a string to be shown as an error message like (e.g., "invalid passphrase"). Blanks must be percent escaped or replaced by @code{+}'. @var{prompt} is either a single @code{X} for a default prompt or the diff --git a/doc/gpg-card.texi b/doc/gpg-card.texi index 3a659e80f..6458598bd 100644 --- a/doc/gpg-card.texi +++ b/doc/gpg-card.texi @@ -546,7 +546,7 @@ be printed; to create a new key anyway the option @samp{--force} can be used. Note that only the private and public keys have been created but no certificates are stored in the key slots. In fact, GnuPG uses its own non-standard method to store just the public key in place of -the the certificate. Other application will not be able to make use +the certificate. Other application will not be able to make use these keys until @command{gpgsm} or another tool has been used to create and store the respective certificates. Let us see what the list command now shows: diff --git a/doc/gpg.texi b/doc/gpg.texi index 8755b9455..446189b4b 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -330,21 +330,21 @@ The status of the verification is indicated by a flag directly following the "sig" tag (and thus before the flags described below. A "!" indicates that the signature has been successfully verified, a "-" denotes a bad signature and a "%" is used if an error occurred while -checking the signature (e.g. a non supported algorithm). Signatures +checking the signature (e.g., a non supported algorithm). Signatures where the public key is not available are not listed; to see their keyids the command @option{--list-sigs} can be used. For each signature listed, there are several flags in between the signature status flag and keyid. These flags give additional information about each key signature. From left to right, they are -the numbers 1-3 for certificate check level (see +the numbers 1--3 for certificate check level (see @option{--ask-cert-level}), "L" for a local or non-exportable signature (see @option{--lsign-key}), "R" for a nonRevocable signature (see the @option{--edit-key} command "nrsign"), "P" for a signature that contains a policy URL (see @option{--cert-policy-url}), "N" for a signature that contains a notation (see @option{--cert-notation}), "X" for an eXpired signature (see @option{--ask-cert-expire}), and the -numbers 1-9 or "T" for 10 and above to indicate trust signature levels +numbers 1--9 or "T" for 10 and above to indicate trust signature levels (see the @option{--edit-key} command "tsign"). @@ -362,9 +362,9 @@ public keys are listed. The variant @option{--locate-external-keys} does not consider a locally existing key and can thus be used to force the refresh of a key via the defined external methods. If a fingerprint is given and -and the methods defined by --auto-key-locate define LDAP servers, the -key is fetched from these resources; defined non-LDAP keyservers are -skipped. +and the methods defined by @option{--auto-key-locate} define LDAP +servers, the key is fetched from these resources; defined non-LDAP +keyservers are skipped. @item --show-keys @@ -405,7 +405,7 @@ description, please see the Card HOWTO at https://gnupg.org/documentation/howtos.html#GnuPG-cardHOWTO . Please note that the command "openpgp" can be used to switch to the OpenPGP application of cards which by default are presenting another -application (e.g. PIV). +application (e.g., PIV). @item --card-status @opindex card-status @@ -589,7 +589,7 @@ corrupted trustdb. Example: Update the trustdb with the ownertrust values stored in @code{files} (or STDIN if not given); existing values will be overwritten. In case of a severely damaged trustdb and if you have a recent backup of the -ownertrust values (e.g. in the file @file{otrust.txt}), you may re-create +ownertrust values (e.g., in the file @file{otrust.txt}), you may re-create the trustdb using these commands: @c man:.RS @example @@ -892,7 +892,7 @@ signing. @item delsig @opindex keyedit:delsig Delete a signature. Note that it is not possible to retract a signature, - once it has been send to the public (i.e. to a keyserver). In that case + once it has been send to the public (i.e., to a keyserver). In that case you better use @code{revsig}. @item revsig @@ -926,7 +926,7 @@ signing. @opindex keyedit:deluid Delete a user ID or photographic user ID. Note that it is not possible to retract a user id, once it has been send to the public - (i.e. to a keyserver). In that case you better use @code{revuid}. + (i.e., to a keyserver). In that case you better use @code{revuid}. @item revuid @opindex keyedit:revuid @@ -1005,13 +1005,13 @@ signing. @item keytocard @opindex keyedit:keytocard Transfer the selected secret subkey (or the primary key if no subkey - has been selected) to a smartcard. The secret key in the keyring will - be replaced by a stub if the key could be stored successfully on the - card and you use the save command later. Only certain key types may be - transferred to the card. A sub menu allows you to select on what card - to store the key. Note that it is not possible to get that key back - from the card - if the card gets broken your secret key will be lost - unless you have a backup somewhere. + has been selected) to a smartcard. The secret key in the keyring + will be replaced by a stub if the key could be stored successfully + on the card and you use the save command later. Only certain key + types may be transferred to the card. A sub menu allows you to + select on what card to store the key. Note that it is not possible + to get that key back from the card --- if the card gets broken your + secret key will be lost unless you have a backup somewhere. @item bkuptocard @var{file} @opindex keyedit:bkuptocard @@ -1046,7 +1046,7 @@ signing. @item delkey @opindex keyedit:delkey Remove a subkey (secondary key). Note that it is not possible to retract - a subkey, once it has been send to the public (i.e. to a keyserver). In + a subkey, once it has been send to the public (i.e., to a keyserver). In that case you better use @code{revkey}. Also note that this only deletes the public part of a key. @@ -1098,7 +1098,7 @@ signing. @item clean @opindex keyedit:clean Compact (by removing all signatures except the selfsig) any user ID - that is no longer usable (e.g. revoked, or expired). Then, remove any + that is no longer usable (e.g., revoked, or expired). Then, remove any signatures that are not usable by the trust calculations. Specifically, this removes any signature that does not validate, any signature that is superseded by a later signature, revoked signatures, @@ -1112,7 +1112,7 @@ signing. @item change-usage @opindex keyedit:change-usage Change the usage flags (capabilities) of the primary key or of - subkeys. These usage flags (e.g. Certify, Sign, Authenticate, + subkeys. These usage flags (e.g., Certify, Sign, Authenticate, Encrypt) are set during key creation. Sometimes it is useful to have the opportunity to change them (for example to add Authenticate) after they have been created. Please take care when @@ -1272,13 +1272,13 @@ behaviour and to change the default configuration. @end menu Long options can be put in an options file (default -"~/.gnupg/gpg.conf"). Short option names will not work - for example, -"armor" is a valid option for the options file, while "a" is not. Do not -write the 2 dashes, but simply the name of the option and any required -arguments. Lines with a hash ('#') as the first non-white-space -character are ignored. Commands may be put in this file too, but that is -not generally useful as the command will execute automatically with -every execution of gpg. +"~/.gnupg/gpg.conf"). Short option names will not work --- for +example, "armor" is a valid option for the options file, while "a" is +not. Do not write the 2 dashes, but simply the name of the option and +any required arguments. Lines with a hash ('#') as the first +non-white-space character are ignored. Commands may be put in this +file too, but that is not generally useful as the command will execute +automatically with every execution of gpg. Please remember that option parsing stops as soon as a non-option is encountered, you can explicitly stop parsing by using the special option @@ -1422,6 +1422,18 @@ give the opposite meaning. The options are: Show all, IETF standard, or user-defined signature notations in the @option{--check-signatures} listings. Defaults to no. + @item show-x509-notations + @opindex list-options:show-x509-notations + Print X.509 certificates embedded in key signatures as PEM data. + This is intended for debugging and the output format may change + without notice. + + @item store-x509-notations + @opindex list-options:store-x509-notations + Store X.509 certificates embedded in key signatures as PEM data + files. The filename consists the 4 byte key ID of the certificate, + a dash, the fingerprint of the key or subkey, and the suffix ".pem". + @item show-keyserver-urls @opindex list-options:show-keyserver-urls Show any preferred keyserver URL in the @@ -1469,10 +1481,10 @@ give the opposite meaning. The options are: @item sort-sigs @opindex list-options:sort-sigs - With --list-sigs and --check-sigs sort the signatures by keyID and - creation time to make it easier to view the history of these - signatures. The self-signature is also listed before other - signatures. Defaults to yes. + With @option{--list-sigs} and @option{--check-sigs} sort the + signatures by keyID and creation time to make it easier to view the + history of these signatures. The self-signature is also listed + before other signatures. Defaults to yes. @end table @@ -1529,12 +1541,12 @@ the opposite meaning. The options are: @itemx --disable-large-rsa @opindex enable-large-rsa @opindex disable-large-rsa -With --generate-key and --batch, enable the creation of RSA secret keys as -large as 8192 bit. Note: 8192 bit is more than is generally -recommended. These large keys don't significantly improve security, -but they are more expensive to use, and their signatures and -certifications are larger. This option is only available if the -binary was build with large-secmem support. +With @option{--generate-key} and @option{--batch}, enable the creation +of RSA secret keys as large as 8192 bit. Note: 8192 bit is more than +is generally recommended. These large keys don't significantly +improve security, but they are more expensive to use, and their +signatures and certifications are larger. This option is only +available if the binary was build with large-secmem support. @item --enable-dsa2 @itemx --disable-dsa2 @@ -1552,9 +1564,9 @@ will be expanded to a filename containing the photo. "%I" does the same, except the file will not be deleted once the viewer exits. Other flags are "%k" for the key ID, "%K" for the long key ID, "%f" for the key fingerprint, "%t" for the extension of the image type -(e.g. "jpg"), "%T" for the MIME type of the image (e.g. "image/jpeg"), +(e.g., "jpg"), "%T" for the MIME type of the image (e.g., "image/jpeg"), "%v" for the single-character calculated validity of the image being -viewed (e.g. "f"), "%V" for the calculated validity as a string (e.g. +viewed (e.g., "f"), "%V" for the calculated validity as a string (e.g., "full"), "%U" for a base32 encoded hash of the user ID, and "%%" for an actual percent sign. If neither %i or %I are present, then the photo will be supplied to the viewer on standard input. @@ -2058,7 +2070,7 @@ default), that keyserver is tried. Note that the creator of the signature uses the option @option{--sig-keyserver-url} to specify the preferred keyserver for data signatures. -3. If the signature has the Signer's UID set (e.g. using +3. If the signature has the Signer's UID set (e.g., using @option{--sender} while creating the signature) a Web Key Directory (WKD) lookup is done. This is the default configuration but can be disabled by removing WKD from the auto-key-locate list or by using the @@ -2086,7 +2098,7 @@ option is ignored if the option @option{--with-colons} is used. @item --keyserver @var{name} @opindex keyserver -This option is deprecated - please use the @option{--keyserver} in +This option is deprecated --- please use the @option{--keyserver} in @file{dirmngr.conf} instead. Use @var{name} as your keyserver. This is the server that @@ -2306,7 +2318,7 @@ suppressed on the command line. @itemx --no-require-secmem @opindex require-secmem Refuse to run if GnuPG cannot get secure memory. Defaults to no -(i.e. run, but give a warning). +(i.e., run, but give a warning). @item --require-cross-certification @@ -2438,7 +2450,7 @@ id used to make the signature and embeds that user ID into the created signature (using OpenPGP's ``Signer's User ID'' subpacket). If the option is given multiple times a suitable user ID is picked. However, if the signing key was specified directly by using a mail address -(i.e. not by using a fingerprint or key ID) this option is used and +(i.e., not by using a fingerprint or key ID) this option is used and the mail address is embedded in the created signature. When verifying a signature @var{mbox} is used to restrict the @@ -2547,7 +2559,7 @@ the @option{--status-fd} line ``PROGRESS'' to provide a value for @item --key-origin @var{string}[,@var{url}] @opindex key-origin gpg can track the origin of a key. Certain origins are implicitly -known (e.g. keyserver, web key directory) and set. For a standard +known (e.g., keyserver, web key directory) and set. For a standard import the origin of the keys imported can be set with this option. To list the possible values use "help" for @var{string}. Some origins can store an optional @var{url} argument. That URL can appended to @@ -2669,12 +2681,12 @@ The available filter types are: @item drop-subkey This filter drops the selected subkeys. - Currently only implemented for --export-filter. + Currently only implemented for @option{--export-filter}. @item drop-sig This filter drops the selected key signatures on user ids. Self-signatures are not considered. - Currently only implemented for --import-filter. + Currently only implemented for @option{--import-filter}. @item select This filter is only implemented by @option{--list-filter}. All @@ -2719,13 +2731,13 @@ The available properties are: @itemx key_created_d The first is the timestamp a public key or subkey packet was created. The second is the same but given as an ISO string, - e.g. "2016-08-17". (drop-subkey) + e.g., "2016-08-17". (drop-subkey) @item key_expires @itemx key_expires_d The expiration time of a public key or subkey or 0 if it does not expire. The second is the same but given as an ISO date string or - an empty string e.g. "2038-01-19". + an empty string e.g., "2038-01-19". @item fpr The hexified fingerprint of the current subkey or primary key. @@ -2758,7 +2770,7 @@ The available properties are: @itemx sig_created_d The first is the timestamp a signature packet was created. The second is the same but given as an ISO date string, - e.g. "2016-08-17". (drop-sig) + e.g., "2016-08-17". (drop-sig) @item sig_expires @itemx sig_expires_d @@ -2884,7 +2896,7 @@ obsolete; it does not harm to use it though. @opindex legacy-list-mode Revert to the pre-2.1 public key list mode. This only affects the human readable output and not the machine interface -(i.e. @code{--with-colons}). Note that the legacy format does not +(i.e., @code{--with-colons}). Note that the legacy format does not convey suitable information for elliptic curves. @item --with-fingerprint @@ -2893,12 +2905,15 @@ Same as the command @option{--fingerprint} but changes only the format of the output and may be used together with another command. @item --with-subkey-fingerprint +@itemx --without-subkey-fingerprint @opindex with-subkey-fingerprint +@opindex without-subkey-fingerprint If a fingerprint is printed for the primary key, this option forces printing of the fingerprint for all subkeys. This could also be achieved by using the @option{--with-fingerprint} twice but by using -this option along with keyid-format "none" a compact fingerprint is -printed. +this option along with the default keyid-format "none" a compact +fingerprint is printed. Since version 2.6.0 this option is active by +default; use the ``without'' variant to disable it. @item --with-v5-fingerprint @opindex with-v5-fingerprint @@ -3002,7 +3017,7 @@ to safely override the algorithm chosen by the recipient key preferences, as GPG will only select an algorithm that is usable by all recipients. The most highly ranked digest algorithm in this list is also used when signing without encryption -(e.g. @option{--clear-sign} or @option{--sign}). +(e.g., @option{--clear-sign} or @option{--sign}). @item --personal-compress-preferences @var{string} @opindex personal-compress-preferences @@ -3013,7 +3028,7 @@ allows the user to safely override the algorithm chosen by the recipient key preferences, as GPG will only select an algorithm that is usable by all recipients. The most highly ranked compression algorithm in this list is also used when there are no recipient keys -to consider (e.g. @option{--symmetric}). +to consider (e.g., @option{--symmetric}). @item --s2k-cipher-algo @var{name} @opindex s2k-cipher-algo @@ -3039,7 +3054,7 @@ of times (see @option{--s2k-count}). Specify how many times the passphrases mangling for symmetric encryption is repeated. This value may range between 1024 and 65011712 inclusive. The default is inquired from gpg-agent. Note -that not all values in the 1024-65011712 range are legal and if an +that not all values in the 1024--65011712 range are legal and if an illegal value is selected, GnuPG will round up to the nearest legal value. This option is only meaningful if @option{--s2k-mode} is set to the default of 3. @@ -3131,6 +3146,15 @@ This option adjusts the compliance mode "de-vs" for stricter key size requirements. For example, a value of 3000 turns rsa2048 and dsa2048 keys into non-VS-NfD compliant keys. +@item --require-pqc-encryption +@opindex require-pqc-encryption +This option forces the use of quantum-resistant encryption algorithms. +If not all public keys are quantum-resistant the encryption will fail. +On decryption a warning is printed for all non-quantum-resistant keys. +As of now the Kyber (ML-KEM768 and ML-KEM1024) algorithms are +considered quantum-resistant; Kyber is always used in a composite +scheme along with a classic ECC algorithm. + @item --require-compliance @opindex require-compliance To check that data has been encrypted according to the rules of the @@ -3209,7 +3233,7 @@ however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given -in C syntax (e.g. 0x0042) or as a comma separated list of flag names. +in C syntax (e.g., 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @@ -3244,7 +3268,7 @@ only useful for certain regression tests. This option is only useful for testing; it sets the system time back or forth to @var{epoch} which is the number of seconds elapsed since the year 1970. Alternatively @var{epoch} may be given as a full ISO -time string (e.g. "20070924T154812"). +time string (e.g., "20070924T154812"). If you suffix @var{epoch} with an exclamation mark (!), the system time will appear to be frozen at the specified time. @@ -3584,7 +3608,7 @@ are: @opindex no-symkey-cache Disable the passphrase cache used for symmetrical en- and decryption. This cache is based on the message specific salt value -(cf. @option{--s2k-mode}). +(cf.@: @option{--s2k-mode}). @item --request-origin @var{origin} @opindex request-origin @@ -3856,6 +3880,12 @@ This option enables a mode in which filenames of the form @file{-&n}, where n is a non-negative decimal number, refer to the file descriptor n and not to a file with that name. +@item --disable-fd-translation +@opindex disable-fd-translation +This option changes the behaviour for all following options to expect +libc file descriptors instead of HANDLE values on the command line. +The option has an effect only on Windows. + @item --no-expensive-trust-checks @opindex no-expensive-trust-checks Experimental use only. @@ -4672,7 +4702,7 @@ If you don't give any of them, no user ID is created. @item Expire-Date: @var{iso-date}|(@var{number}[d|w|m|y]) Set the expiration date for the key (and the subkey). It may either -be entered in ISO date format (e.g. "20000815T145012") or as number of +be entered in ISO date format (e.g., "20000815T145012") or as number of days, weeks, month or years after the creation date. The special notation "seconds=N" is also allowed to specify a number of seconds since creation. Without a letter days are assumed. Note that there diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index d00ad447e..1316318a6 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -136,7 +136,7 @@ Run in server mode and wait for commands on the @code{stdin}. Behave as a Dirmngr client issuing the request @var{command} with the optional list of @var{args}. The output of the Dirmngr is printed stdout. Please note that file names given as arguments should have an -absolute file name (i.e. commencing with @code{/}) because they are +absolute file name (i.e., commencing with @code{/}) because they are passed verbatim to the Dirmngr and the working directory of the Dirmngr might not be the same as the one of this client. Currently it is not possible to pass data via stdin to the Dirmngr. @var{command} @@ -259,7 +259,7 @@ optional @var{pattern}. Those pattern consist of a list of user ids @option{--armor} option a few informational lines are prepended before each block. There is one limitation: As there is no commonly agreed upon way to pack more than one certificate into an ASN.1 structure, -the binary export (i.e. without using @option{armor}) works only for +the binary export (i.e., without using @option{armor}) works only for the export of one certificate. Thus it is required to specify a @var{pattern} which yields exactly one certificate. Ephemeral certificate are only exported if all @var{pattern} are given as @@ -462,7 +462,7 @@ line of the @file{trustlist.txt} @opindex force-crl-refresh Tell the dirmngr to reload the CRL for each request. For better performance, the dirmngr will actually optimize this by suppressing -the loading for short time intervals (e.g. 30 minutes). This option +the loading for short time intervals (e.g., 30 minutes). This option is useful to make sure that a fresh CRL is available for certificates hold in the keybox. The suggested way of doing this is by using it along with the option @option{--with-validation} for a key listing @@ -539,7 +539,7 @@ Create PEM encoded output. Default is binary output. @item --base64 @opindex base64 -Create Base-64 encoded output; i.e. PEM without the header lines. +Create Base-64 encoded output; i.e., PEM without the header lines. @item --assume-armor @opindex assume-armor @@ -639,11 +639,11 @@ done with @code{--with-colons}. @item --no-pretty-dn @opindex no-pretty-dn By default gpgsm prints distinguished names (DNs) like the Issuer or -Subject in a more readable format (e.g. using a well defined order of +Subject in a more readable format (e.g., using a well defined order of the parts). However, this format can't be used as input strings. This option reverts printing to standard RFC-2253 format and thus -avoids the need to use --dump-cert or --with-colons to get the -``real'' name. +avoids the need to use @option{--dump-cert} or @option{--with-colons} +to get the ``real'' name. @end table @@ -754,7 +754,7 @@ key database clear of unneeded certificates stored on smartcards. This option is only useful for testing; it sets the system time back or forth to @var{epoch} which is the number of seconds elapsed since the year 1970. Alternatively @var{epoch} may be given as a full ISO time string -(e.g. "20070924T154812"). +(e.g., "20070924T154812"). @item --with-ephemeral-keys @opindex with-ephemeral-keys @@ -770,6 +770,18 @@ list of flag names and are OR-ed together. The special flag "none" clears the list and allows one to start over with an empty list. To get a list of available flags the sole word "help" can be used. +@item --enable-special-filenames +@opindex enable-special-filenames +This option enables a mode in which filenames of the form +@file{-&n}, where n is a non-negative decimal number, +refer to the file descriptor n and not to a file with that name. + +@item --disable-fd-translation +@opindex disable-fd-translation +This option changes the behaviour for all following options to expect +libc file descriptors instead of HANDLE values on the command line. +The option has an effect only on Windows. + @item --debug-level @var{level} @opindex debug-level Select the debug level for investigating problems. @var{level} may be @@ -801,7 +813,7 @@ however carefully selected to best aid in debugging. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given -in C syntax (e.g. 0x0042) or as a comma separated list of flag names. +in C syntax (e.g., 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @@ -974,9 +986,9 @@ This is plain text file with a few help entries used with @command{gpg} and @command{gpgsm}. The standard file has English help texts; to install localized versions use filenames like @file{help.LL.txt} with LL denoting the locale. GnuPG comes with a set of predefined help -files in the data directory (e.g. @file{@value{DATADIR}/gnupg/help.de.txt}) +files in the data directory (e.g., @file{@value{DATADIR}/gnupg/help.de.txt}) and allows overriding of any help item by help files stored in the -system configuration directory (e.g. @file{@value{SYSCONFDIR}/help.de.txt}). +system configuration directory (e.g., @file{@value{SYSCONFDIR}/help.de.txt}). For a reference of the help file's syntax, please see the installed @file{help.txt} file. @@ -987,11 +999,10 @@ This file is a collection of common certificates used to populated a newly created @file{pubring.kbx}. An administrator may replace this file with a custom one. The format is a concatenation of PEM encoded X.509 certificates. This global file is installed in the data directory -(e.g. @file{@value{DATADIR}/com-certs.pem}). +(e.g., @file{@value{DATADIR}/com-certs.pem}). @end table -@c man:.RE Note that on larger installations, it is useful to put predefined files into the directory @file{/etc/skel/.gnupg/} so that newly created users start up with a working configuration. For existing users a small @@ -1100,7 +1111,7 @@ of a transfer error, a program error or tampering with the message). @end table @item Error verifying a signature -For some reason the signature could not be verified, i.e. it cannot be +For some reason the signature could not be verified, i.e., it cannot be decided whether the signature is valid or invalid. A common reason for this is a missing certificate. @@ -1281,7 +1292,7 @@ provides a regular command line interface which exhibits a full client to this protocol (but uses internal linking). To start @command{gpgsm} as a server the command line the option @code{--server} must be used. Additional options are provided to -select the communication method (i.e. the name of the socket). +select the communication method (i.e., the name of the socket). We assume that the connection has already been established; see the Assuan manual for details. @@ -1345,7 +1356,7 @@ correct. OUTPUT FD[=@var{n}] [--armor|--base64] @end example -Set the file descriptor to be used for the output (i.e. the encrypted +Set the file descriptor to be used for the output (i.e., the encrypted message). Obviously the pipe must be open at that point, the server establishes its own end. If the server returns an error the client should consider this session failed. @@ -1388,11 +1399,11 @@ The decryption is done by using the command DECRYPT @end example -It performs the decrypt operation after doing some check on the internal -state (e.g. that all needed data has been set). Because it utilizes -the GPG-Agent for the session key decryption, there is no need to ask -the client for a protecting passphrase - GpgAgent takes care of this by -requesting this from the user. +It performs the decrypt operation after doing some check on the +internal state (e.g., that all needed data has been set). Because it +utilizes the GPG-Agent for the session key decryption, there is no +need to ask the client for a protecting passphrase --- GpgAgent takes +care of this by requesting this from the user. @node GPGSM SIGN diff --git a/doc/gpgv.texi b/doc/gpgv.texi index 54ab23383..cbaea40e5 100644 --- a/doc/gpgv.texi +++ b/doc/gpgv.texi @@ -91,7 +91,7 @@ Add @var{file} to the list of keyrings. If @var{file} begins with a tilde and a slash, these are replaced by the HOME directory. If the filename does not contain a slash, it is assumed to be in the -home-directory ("~/.gnupg" if --homedir is not used). +home-directory ("~/.gnupg" if @option{--homedir} is not used). @item --output @var{file} @itemx -o @var{file} @@ -184,10 +184,18 @@ If set directory used instead of "~/.gnupg". @mansect files @subsection FILES -@table @asis +Default keyring file is expected in the GnuPG home directory +(@pxref{option --homedir}, @code{GNUPGHOME}). + +@table @file +@item ~/.gnupg/trustedkeys.kbx +@efindex trustedkeys.kbx +The default keyring with the allowed keys, using the new keybox format. @item ~/.gnupg/trustedkeys.gpg -The default keyring with the allowed keys. +@efindex trustedkeys.gpg +When @file{trustedkeys.kbx} is not available, the default keyring with +the allowed keys, using a legacy format. @end table diff --git a/doc/howto-create-a-server-cert.texi b/doc/howto-create-a-server-cert.texi index 30e28bdd0..de5a9470f 100644 --- a/doc/howto-create-a-server-cert.texi +++ b/doc/howto-create-a-server-cert.texi @@ -80,7 +80,7 @@ would anyway ignore such a request. Thus just hit enter. If you want to create a client certificate for email encryption, this would be the place to enter your mail address -(e.g. @email{joe@@example.org}). You may enter as many addresses as you like, +(e.g., @email{joe@@example.org}). You may enter as many addresses as you like, however the CA may not accept them all or reject the entire request. @cartouche diff --git a/agent/keyformat.txt b/doc/keyformat.txt index e0c4df0f0..dadfed4eb 100644 --- a/agent/keyformat.txt +++ b/doc/keyformat.txt @@ -59,7 +59,7 @@ A name must start with a letter and end with a colon. Valid characters are all ASCII letters, numbers and the hyphen. Comparison of names is done case insensitively. Names may be used several times to represent an array of values. Note that the name "Key" is special -in that it is madandory must occur only once. +in that it is mandatory and must occur only once. *** Values Values are UTF-8 encoded strings. Values can be wrapped at any point, @@ -156,7 +156,7 @@ dialog window when card is not available. When the value is "no", a card operation is refused with GPG_ERR_UNUSABLE_SECKEY error. *** Backup-info -This gives information for a backup of the key. The follwoing fields +This gives information for a backup of the key. The following fields are space delimited: - Hexified keygrip (uppercase) to make it easy to identify the @@ -345,7 +345,7 @@ The currently defined protection modes are: ** Shadowed Private Key Format To keep track of keys stored on IC cards we use a third format for -private kyes which are called shadow keys as they are only a reference +private keys which are called shadow keys as they are only a reference to keys stored on a token: (shadowed-private-key @@ -395,7 +395,7 @@ This format is used to transfer keys between gpg and gpg-agent. * PUBKEYALGO is a Libgcrypt algo name * CURVENAME is the name of the curve - only used with ECC. * P1 .. PN are the parameters; the public parameters are never encrypted - the secrect key parameters are encrypted if the "protection" list is + the secret key parameters are encrypted if the "protection" list is given. To make this more explicit each parameter is preceded by a flag "_" for cleartext or "e" for encrypted text. * CSUM is the deprecated 16 bit checksum as defined by OpenPGP. This @@ -404,7 +404,7 @@ This format is used to transfer keys between gpg and gpg-agent. the old 16 bit checksum (above) is used and if it is "none" no protection at all is used. * PROTALGO is a Libgcrypt style cipher algorithm name - * IV is the initialization verctor. + * IV is the initialization vector. * S2KMODE is the value from RFC-4880. * S2KHASH is a libgcrypt style hash algorithm identifier. * S2KSALT is the 8 byte salt @@ -492,7 +492,7 @@ with "encrypted_octet_string" decoding to: (hash sha1 #0102030405060708091011121314151617181920#) ) -To compute the hash this S-expression (in canoncical format) was +To compute the hash this S-expression (in canonical format) was hashed: ((desc "List of system passphrases") diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index d57326125..cbb22225d 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -161,7 +161,7 @@ helpers to debug problems. @item --debug @var{flags} @opindex debug Set debug flags. All flags are or-ed and @var{flags} may be given -in C syntax (e.g. 0x0042) or as a comma separated list of flag names. +in C syntax (e.g., 0x0042) or as a comma separated list of flag names. To get a list of all supported flags the single word "help" can be used. This option is only useful for debugging and the behavior may change at any time without notice. @@ -238,7 +238,7 @@ this option only if you know what you are doing. Use @var{library} to access the smartcard reader. The current default on Unix is @file{libpcsclite.so} and on Windows @file{winscard.dll}. Instead of using this option you might also want to install a symbolic -link to the default file name (e.g. from @file{libpcsclite.so.1}). +link to the default file name (e.g., from @file{libpcsclite.so.1}). A Unicode file name may not be used on Windows. @item --disable-ccid @@ -503,7 +503,7 @@ will return an error when a card change has been detected and the use of this function is therefore required. Background: We want to keep the client clear of handling card changes -between operations; i.e. the client can assume that all operations are +between operations; i.e., the client can assume that all operations are done on the same card unless he call this function. @example @@ -717,7 +717,7 @@ reset the card. This is used by gpg-agent to reuse a primary pipe connection and may be used by clients to backup from a conflict in the serial -command; i.e. to select another application. +command; i.e., to select another application. diff --git a/doc/specify-user-id.texi b/doc/specify-user-id.texi index 64e354bdf..1dc91b6d8 100644 --- a/doc/specify-user-id.texi +++ b/doc/specify-user-id.texi @@ -39,7 +39,7 @@ using the option @option{--with-colons}. @item By fingerprint. This format is deduced from the length of the string and its content or the @code{0x} prefix. Note, that only the 20 byte version fingerprint -is available with @command{gpgsm} (i.e. the SHA-1 hash of the +is available with @command{gpgsm} (i.e., the SHA-1 hash of the certificate). When using @command{gpg} an exclamation mark (!) may be appended to @@ -88,7 +88,7 @@ with left and right angles. @item By partial match on an email address. This is indicated by prefixing the search string with an @code{@@}. This uses a substring search but considers only the mail address -(i.e. inside the angle brackets). +(i.e., inside the angle brackets). @cartouche @example diff --git a/doc/tools.texi b/doc/tools.texi index da537b6d0..9ce0e6bb8 100644 --- a/doc/tools.texi +++ b/doc/tools.texi @@ -124,7 +124,7 @@ $ watchgnupg --time-only @end example This waits for connections on the local socket -(e.g. @file{/var/run/user/1234/gnupg/S.log}) and shows all log +(e.g., @file{/var/run/user/1234/gnupg/S.log}) and shows all log entries. To make this work the option @option{log-file} needs to be used with all modules which logs are to be shown. The suggested entry for the configuration files is: @@ -133,8 +133,8 @@ for the configuration files is: log-file socket:// @end example -If the default socket as given above and returned by "echo $(gpgconf ---list-dirs socketdir)/S.log" is not desired an arbitrary socket name +If the default socket as given above and returned by @code{"echo $(gpgconf +--list-dirs socketdir)/S.log"} is not desired an arbitrary socket name can be specified, for example @file{socket:///home/foo/bar/mysocket}. For debugging purposes it is also possible to do remote logging. Take care if you use this feature because the information is send in the @@ -1151,13 +1151,21 @@ More fields may be added in future to the output. Under Windows this file is used to install GnuPG as a portable application. An empty file named @file{gpgconf.ctl} is expected in - the same directory as the tool @file{gpgconf.exe}. The root of the + the same directory as the tool @file{gpgconf.exe} or the file must + have a keyword @code{portable} with the value true. The root of the installation is then that directory; or, if @file{gpgconf.exe} has been installed directly below a directory named @file{bin}, its parent directory. You also need to make sure that the following directories exist and are writable: @file{ROOT/home} for the GnuPG home and @file{ROOT@value{LOCALCACHEDIR}} for internal cache files. + On both platforms the keyword @code{gnupg} can be used to change the + standard home directory. For example a value of "gnupg-vsd" will + change the default home directory on unix from @file{~/.gnupg} to + @file{~/.gnupg-vsd}. The socket directory is changed accordingly + unless the @code{socketdir} keyword has been used. On Windows the + Registry keys are modified as well. + @item /etc/gnupg/gpgconf.conf @cindex gpgconf.conf @@ -1284,7 +1292,7 @@ Alternatively an arbitrary string may be used to identify a passphrase; it is suggested that such a string is prefixed with the name of the application (e.g @code{foo:12346}). Scripts should always use the option @option{--with-colons}, which provides the keygrip in a -"grp" line (cf. @file{doc/DETAILS})/ +"grp" line (cf.@: @file{doc/DETAILS})/ @noindent One of the following command options must be given: @@ -1765,7 +1773,7 @@ The return value of this command is @table @code @item 0 -The certificate under question is valid; i.e. there is a valid CRL +The certificate under question is valid; i.e., there is a valid CRL available and it is not listed there or the OCSP request returned that that certificate is valid. @@ -2088,9 +2096,9 @@ This option is deprecated in favor of option @option{--directory}. @item --no-compress @opindex no-compress -This option tells gpg to disable compression (i.e. using option -z0). +This option tells gpg to disable compression (i.e., using option -z0). It is useful for archiving only large files which are are already -compressed (e.g. a set of videos). +compressed (e.g., a set of videos). @item --gpg @var{gpgcmd} @opindex gpg @@ -2103,9 +2111,10 @@ Pass the specified extra options to @command{gpg}. @item --tar-args @var{args} @opindex tar-args Assume @var{args} are standard options of the command @command{tar} -and parse them. The only supported tar options are "--directory", -"--files-from", and "--null" This is an obsolete options because those -supported tar options can also be given directly. +and parse them. The only supported tar options are +@option{--directory}, @option{--files-from}, and @option{--null}. +This is an obsolete options because those supported tar options can +also be given directly. @item --tar @var{command} @opindex tar diff --git a/doc/wks.texi b/doc/wks.texi index 2870e52d8..bfdd069f2 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -214,7 +214,7 @@ The default is @file{openpgpkey}. @opindex blacklist This option is used to exclude certain mail addresses from a mirror operation. The format of @var{file} is one mail address (just the -addrspec, e.g. "postel@@isi.edu") per line. Empty lines and lines +addrspec, e.g., "postel@@isi.edu") per line. Empty lines and lines starting with a '#' are ignored. @item --add-revocs diff --git a/g10/build-packet.c b/g10/build-packet.c index 19a13760a..606c5c2d8 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -433,7 +433,7 @@ sos_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten) * Write an opaque string to the output stream without length info. */ gpg_error_t -gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) +gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a) { int rc; @@ -452,6 +452,45 @@ gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) } +/* + * Write an opaque MPI string with a four-byte octet count to the + * output stream. If R_NWRITTEN is not NULL the number of written + * bytes is stored there. OUT may be NULL in which case only + * R_NWRITTEN is updated and error checking is done. + */ +gpg_error_t +gpg_mpi_write_opaque_32 (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten) +{ + gpg_error_t err; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits, nbytes; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + nbytes = (nbits + 7)/8; + if (out) + { + write_32 (out, nbytes); + err = p ? iobuf_write (out, p, nbytes) : 0; + } + else + err = 0; + if (r_nwritten) + *r_nwritten = 4 + (p? nbytes : 0); + } + else + { + err = gpg_error (GPG_ERR_BAD_MPI); + if (r_nwritten) + *r_nwritten = 0; + } + + return err; +} + + /* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) @@ -639,8 +678,14 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) { if ( (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0)) || (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0)) - || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) - err = gpg_mpi_write_nohdr (a, pk->pkey[i]); + || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)) + || (pk->pubkey_algo == PUBKEY_ALGO_KYBER && (i == 0))) + err = gpg_mpi_write_opaque_nohdr (a, pk->pkey[i]); + else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + { + /* Write a four-octet count prefixed Kyber public key. */ + err = gpg_mpi_write_opaque_32 (a, pk->pkey[2], NULL); + } else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) @@ -779,9 +824,15 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) for (j=i; j < nskey; j++ ) { - if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA - || pk->pubkey_algo == PUBKEY_ALGO_EDDSA - || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && j == 4) + { + if ((err=gpg_mpi_write_opaque_32 (NULL,pk->pkey[j], &n))) + goto leave; + } + else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_EDDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH + || pk->pubkey_algo == PUBKEY_ALGO_KYBER) { if ((err = sos_write (NULL, pk->pkey[j], &n))) goto leave; @@ -798,16 +849,26 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) } for ( ; i < nskey; i++ ) - if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA - || pk->pubkey_algo == PUBKEY_ALGO_EDDSA - || pk->pubkey_algo == PUBKEY_ALGO_ECDH) - { - if ((err = sos_write (a, pk->pkey[i], NULL))) - goto leave; - } - else - if ((err = gpg_mpi_write (a, pk->pkey[i], NULL))) - goto leave; + { + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 4) + { + err = gpg_mpi_write_opaque_32 (a, pk->pkey[i], NULL); + if (err) + goto leave; + } + else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_EDDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + if ((err = sos_write (a, pk->pkey[i], NULL))) + goto leave; + } + else + { + if ((err = gpg_mpi_write (a, pk->pkey[i], NULL))) + goto leave; + } + } write_16 (a, ski->csum ); } @@ -921,9 +982,19 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) for (i=0; i < n && !rc ; i++ ) { - if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) - rc = gpg_mpi_write_nohdr (a, enc->data[i]); - else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH) + /* For Kyber we need to insert the algo before the final data + * element because it is not stored in the data array. */ + if (enc->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + iobuf_put (a, enc->seskey_algo); + + if (i == 1 && enc->pubkey_algo == PUBKEY_ALGO_ECDH) + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[i]); + else if (i == 1 && enc->pubkey_algo == PUBKEY_ALGO_KYBER) + rc = gpg_mpi_write_opaque_32 (a, enc->data[i], NULL); + else if (i == 2 && enc->pubkey_algo == PUBKEY_ALGO_KYBER) + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[i]); + else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH + || enc->pubkey_algo == PUBKEY_ALGO_KYBER) rc = sos_write (a, enc->data[i], NULL); else rc = gpg_mpi_write (a, enc->data[i], NULL); @@ -1748,6 +1819,72 @@ sig_to_notation(PKT_signature *sig) return list; } + +/* Return a list of notation data matching NAME. The caller needs to + * to free the list using free_notation. Other than sig_to_notation + * this function does not return the notation in human readable format + * but always returns the raw data. The human readable flag is set + * anyway set but .value is always NULL. */ +struct notation * +search_sig_notations (PKT_signature *sig, const char *name) +{ + const byte *p; + size_t len; + int seq = 0; + int crit; + notation_t list = NULL; + + while((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_NOTATION, &len, &seq, &crit))) + { + int n1,n2; + struct notation *n=NULL; + + if (len < 8) + { + log_info (_("WARNING: invalid notation data found\n")); + continue; + } + + /* name length. */ + n1=(p[4]<<8)|p[5]; + /* value length. */ + n2=(p[6]<<8)|p[7]; + + if (8 + n1 + n2 != len) + { + log_info (_("WARNING: invalid notation data found\n")); + continue; + } + + if (!name) + ; /* Return everything. */ + else if (n1 != strlen (name) || memcmp (p+8, name, n1)) + continue; /* Not the requested name. */ + + + n = xmalloc_clear (sizeof *n); + n->name = xmalloc (n1+1); + + memcpy (n->name,p + 8, n1); + n->name[n1]='\0'; + + /* In any case append a Nul. */ + n->bdat = xmalloc (n2+1); + memcpy (n->bdat, p + 8 + n1, n2); + n->bdat[n2] = '\0'; + n->blen = n2; + n->flags.human = !!(p[0] & 0x80); + + n->flags.critical = crit; + + n->next = list; + list = n; + } + + return list; +} + + /* Release the resources associated with the *list* of notations. To release a single notation, make sure that notation->next is NULL. */ diff --git a/g10/call-agent.c b/g10/call-agent.c index 69207566e..cfd4c086a 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -251,7 +251,8 @@ start_agent (ctrl_t ctrl, int flag_for_card) opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, NULL); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { @@ -1079,6 +1080,12 @@ agent_keytotpm (ctrl_t ctrl, const char *hexgrip) snprintf(line, DIM(line), "KEYTOTPM %s\n", hexgrip); + if (strchr (hexgrip, ',')) + { + log_error ("storing a part of a dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + rc = start_agent (ctrl, 0); if (rc) return rc; @@ -1108,6 +1115,13 @@ agent_keytocard (const char *hexgrip, int keyno, int force, memset (&parm, 0, sizeof parm); + if (strchr (hexgrip, ',')) + { + log_error ("storing a part of a dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + + snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s%s%s", force?"--force ": "", hexgrip, serialno, keyno, timestamp, ecdh_param_str? " ":"", ecdh_param_str? ecdh_param_str:""); @@ -2239,9 +2253,9 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; - char *hexgrip; - + char *hexgrip, *p; struct keyinfo_data_parm_s keyinfo; + int result, result2; memset (&keyinfo, 0, sizeof keyinfo); @@ -2252,28 +2266,64 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return 0; + if ((p=strchr (hexgrip, ','))) + *p++ = 0; snprintf (line, sizeof line, "KEYINFO %s", hexgrip); - xfree (hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) - return 0; + result = 0; + else if (keyinfo.card_available) + result = 4; + else if (keyinfo.passphrase_cached) + result = 3; + else if (keyinfo.is_smartcard) + result = 2; + else + result = 1; - if (keyinfo.card_available) - return 4; + if (!p) + { + xfree (hexgrip); + return result; /* Not a dual algo - we are ready. */ + } - if (keyinfo.passphrase_cached) - return 3; + /* Now check the second keygrip. */ + memset (&keyinfo, 0, sizeof keyinfo); + snprintf (line, sizeof line, "KEYINFO %s", p); - if (keyinfo.is_smartcard) - return 2; + err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, + keyinfo_status_cb, &keyinfo); + xfree (keyinfo.serialno); + if (err) + result2 = 0; + else if (keyinfo.card_available) + result2 = 4; + else if (keyinfo.passphrase_cached) + result2 = 3; + else if (keyinfo.is_smartcard) + result2 = 2; + else + result2 = 1; - return 1; + xfree (hexgrip); + + if (result == result2) + return result; /* Both keys have the same status. */ + else if (!result && result2) + return 0; /* Only first key available - return no key. */ + else if (result && !result2) + return 0; /* Only second key not availabale - return no key. */ + else if (result == 4 || result == 2) + return result; /* First key on card - don't care where the second is. */ + else + return result; } + /* Ask the agent whether a secret key is available for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ gpg_error_t @@ -2285,6 +2335,8 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) kbnode_t kbctx, node; int nkeys; /* (always zero in secret_keygrips mode) */ unsigned char grip[KEYGRIP_LEN]; + unsigned char grip2[KEYGRIP_LEN]; + int grip2_valid; const unsigned char *s; unsigned int n; @@ -2340,22 +2392,30 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) if (ctrl && ctrl->secret_keygrips) { /* We got an array with all secret keygrips. Check this. */ - err = keygrip_from_pk (node->pkt->pkt.public_key, grip); + err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; + err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); + if (err && gpg_err_code (err) != GPG_ERR_FALSE) + return err; + grip2_valid = !err; + for (s=ctrl->secret_keygrips, n = 0; n < ctrl->secret_keygrips_len; s += 20, n += 20) { if (!memcmp (s, grip, 20)) return 0; + if (grip2_valid && !memcmp (s, grip2, 20)) + return 0; } err = gpg_error (GPG_ERR_NO_SECKEY); /* Keep on looping over the keyblock. Never bump nkeys. */ } else { - if (nkeys && ((p - line) + 41) > (ASSUAN_LINELENGTH - 2)) + if (nkeys + && ((p - line) + 4*KEYGRIP_LEN+1+1) > (ASSUAN_LINELENGTH - 2)) { err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); @@ -2365,13 +2425,24 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) nkeys = 0; } - err = keygrip_from_pk (node->pkt->pkt.public_key, grip); + err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; + + err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); + if (err && gpg_err_code (err) != GPG_ERR_FALSE) + return err; + if (!err) /* Add the second keygrip from dual algos. */ + { + *p++ = ' '; + bin2hex (grip2, 20, p); + p += 40; + nkeys++; + } } } @@ -2398,6 +2469,7 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct keyinfo_data_parm_s keyinfo; + const char *s; memset (&keyinfo, 0,sizeof keyinfo); @@ -2407,10 +2479,20 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, if (err) return err; - if (!hexkeygrip || strlen (hexkeygrip) != 40) + /* FIXME: Support dual keys. Maybe under the assumption that the + * first key might be on a card. */ + if (!hexkeygrip) + return gpg_error (GPG_ERR_INV_VALUE); + s = strchr (hexkeygrip, ','); + if (!s) + s = hexkeygrip + strlen (hexkeygrip); + if (s - hexkeygrip != 40) return gpg_error (GPG_ERR_INV_VALUE); - snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip); + /* Note that for a dual algo we only get info for the first key. + * FIXME: We need to see how we can show the status of the second + * key in a key listing. */ + snprintf (line, DIM(line), "KEYINFO %.40s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); @@ -2796,6 +2878,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, membuf_t data; size_t n, len; char *p, *buf, *endp; + const char *keygrip2 = NULL; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); @@ -2804,13 +2887,26 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; - if (!keygrip || strlen(keygrip) != 40 - || !s_ciphertext || !r_buf || !r_buflen || !r_padding) + if (!keygrip || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; + /* Parse the keygrip in case of a dual algo. */ + keygrip2 = strchr (keygrip, ','); + if (!keygrip2) + keygrip2 = keygrip + strlen (keygrip); + if (keygrip2 - keygrip != 40) + return gpg_error (GPG_ERR_INV_VALUE); + if (*keygrip2) + { + keygrip2++; + if (strlen (keygrip2) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + } + + err = start_agent (ctrl, 0); if (err) return err; @@ -2821,11 +2917,19 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, if (err) return err; - snprintf (line, sizeof line, "SETKEY %s", keygrip); + snprintf (line, sizeof line, "SETKEY %.40s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; + if (*keygrip2) + { + snprintf (line, sizeof line, "SETKEY --another %.40s", keygrip2); + err = assuan_transact (agent_ctx, line, NULL, NULL,NULL,NULL,NULL,NULL); + if (err) + return err; + } + if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); @@ -2844,7 +2948,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); if (err) return err; - err = assuan_transact (agent_ctx, "PKDECRYPT", + err = assuan_transact (agent_ctx, + *keygrip2? "PKDECRYPT --kem=PQC-PGP":"PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); @@ -3174,6 +3279,7 @@ agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, return err; } + /* FIXME: Shall we add support to DELETE_KEY for dual keys? */ snprintf (line, DIM(line), "DELETE_KEY%s %s", force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index c47bf0928..d00f61450 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -166,7 +166,8 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx) err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL /*gpg_status2*/, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c index 121b7aa2a..bc3806f0b 100644 --- a/g10/call-keyboxd.c +++ b/g10/call-keyboxd.c @@ -94,8 +94,6 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl) log_error ("oops: trying to cleanup an active keyboxd context\n"); else { - kbx_client_data_release (kbl->kcd); - kbl->kcd = NULL; if (kbl->ctx && in_transaction) { /* This is our hack to commit the changes done during a @@ -112,6 +110,15 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl) } assuan_release (kbl->ctx); kbl->ctx = NULL; + /* + * Since there may be pipe output FD sent to the server (so + * that it can receive data through the pipe), we should + * release the assuan connection before releasing KBL->KCD. + * This way, the data receiving thread can finish cleanly, + * and we can join the thread. + */ + kbx_client_data_release (kbl->kcd); + kbl->kcd = NULL; } xfree (kbl); } @@ -143,7 +150,8 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) { @@ -223,7 +231,7 @@ open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl) return err; } - err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 1); + err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 0); if (err) { assuan_release (kbl->ctx); diff --git a/g10/card-util.c b/g10/card-util.c index a66c2e3de..7df505f62 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -28,9 +28,7 @@ # include <readline/readline.h> #endif /*HAVE_LIBREADLINE*/ -#if GNUPG_MAJOR_VERSION != 1 # include "gpg.h" -#endif /*GNUPG_MAJOR_VERSION != 1*/ #include "../common/util.h" #include "../common/i18n.h" #include "../common/ttyio.h" @@ -39,11 +37,7 @@ #include "main.h" #include "keyserver-internal.h" -#if GNUPG_MAJOR_VERSION == 1 -# include "cardglue.h" -#else /*GNUPG_MAJOR_VERSION!=1*/ -# include "call-agent.h" -#endif /*GNUPG_MAJOR_VERSION!=1*/ +#include "call-agent.h" #define CONTROL_D ('D' - 'A' + 1) @@ -949,14 +943,6 @@ get_data_from_file (const char *fname, char **r_buffer) *r_buffer = NULL; fp = es_fopen (fname, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif if (!fp) { tty_printf (_("can't open '%s': %s\n"), fname, strerror (errno)); @@ -992,14 +978,6 @@ put_data_to_file (const char *fname, const void *buffer, size_t length) estream_t fp; fp = es_fopen (fname, "wb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif if (!fp) { tty_printf (_("can't create '%s': %s\n"), fname, strerror (errno)); diff --git a/g10/dearmor.c b/g10/dearmor.c index c0bd9ecf6..f6bb59ef6 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -63,7 +63,7 @@ dearmor_file( const char *fname ) push_armor_filter ( afx, inp ); - if( (rc = open_outfile (-1, fname, 0, 0, &out)) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, fname, 0, 0, &out)) ) goto leave; iobuf_copy (out, inp); @@ -107,7 +107,7 @@ enarmor_file( const char *fname ) } - if( (rc = open_outfile (-1, fname, 1, 0, &out )) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, fname, 1, 0, &out )) ) goto leave; afx->what = 4; diff --git a/g10/decrypt.c b/g10/decrypt.c index cb9e36a93..b30359af4 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -100,7 +100,8 @@ decrypt_message (ctrl_t ctrl, const char *filename) /* Same as decrypt_message but takes a file descriptor for input and output. */ gpg_error_t -decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd) +decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, + gnupg_fd_t output_fd) { #ifdef HAVE_W32_SYSTEM /* No server mode yet. */ @@ -138,13 +139,25 @@ decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd) return err; } - opt.outfp = es_fdopen_nc (output_fd, "wb"); + if (is_secured_file (output_fd)) + { + char xname[64]; + + err = gpg_error (GPG_ERR_EPERM); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd)); + log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); + iobuf_close (fp); + release_progress_context (pfx); + return err; + } + + opt.outfp = open_stream_nc (output_fd, "w"); if (!opt.outfp) { char xname[64]; err = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", output_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); iobuf_close (fp); release_progress_context (pfx); diff --git a/g10/ecdh.c b/g10/ecdh.c index eb14154a1..279508bec 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -156,11 +156,11 @@ build_kdf_params (unsigned char kdf_params[256], size_t *r_size, return gpg_error_from_syserror (); /* variable-length field 1, curve name OID */ - err = gpg_mpi_write_nohdr (obuf, pkey[0]); + err = gpg_mpi_write_opaque_nohdr (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ - err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); + err = (err ? err : gpg_mpi_write_opaque_nohdr (obuf, pkey[2])); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp (or first 20 octets of fp) */ @@ -524,8 +524,7 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], size_t nbytes; byte *data_buf; int data_buf_size; - byte *in; - const void *p; + const unsigned char *p; unsigned int nbits; *r_result = NULL; @@ -538,7 +537,7 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], nbytes = (nbits+7)/8; data_buf_size = nbytes; - if ((data_buf_size & 7) != 1) + if ((data_buf_size & 7) != 1 || data_buf_size <= 1 + 8) { log_error ("can't use a shared secret of %d bytes for ecdh\n", data_buf_size); @@ -546,7 +545,10 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], return gpg_error (GPG_ERR_BAD_DATA); } - data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8); + /* The first octet is for length. It's longer than the result + because of one additional block of AESWRAP. */ + data_buf_size -= 1 + 8; + data_buf = xtrymalloc_secure (data_buf_size); if (!data_buf) { err = gpg_error_from_syserror (); @@ -560,22 +562,18 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } - memcpy (data_buf, p, nbytes); - if (data_buf[0] != nbytes-1) + if (p[0] != nbytes-1) { log_error ("ecdh inconsistent size\n"); xfree (data_buf); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } - in = data_buf+data_buf_size; - data_buf_size = data_buf[0]; if (DBG_CRYPTO) - log_printhex (data_buf+1, data_buf_size, "ecdh decrypting :"); + log_printhex (p+1, nbytes-1, "ecdh decrypting :"); - err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, - data_buf_size); + err = gcry_cipher_decrypt (hd, data_buf, data_buf_size, p+1, nbytes-1); gcry_cipher_close (hd); if (err) { @@ -585,10 +583,8 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], return err; } - data_buf_size -= 8; - if (DBG_CRYPTO) - log_printhex (in, data_buf_size, "ecdh decrypted to :"); + log_printhex (data_buf, data_buf_size, "ecdh decrypted to :"); /* Padding is removed later. */ /* if (in[data_buf_size-1] > 8 ) */ @@ -598,7 +594,8 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], /* return gpg_error (GPG_ERR_BAD_KEY); */ /* } */ - err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, data_buf, + data_buf_size, NULL); xfree (data_buf); if (err) { diff --git a/g10/encrypt.c b/g10/encrypt.c index b335b9797..8ce6164ce 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -507,7 +507,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /**/ : "CFB"); } - if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out ))) + if (rc || (rc = open_outfile (GNUPG_INVALID_FD, filename, opt.armor? 1:0, + 0, &out ))) { iobuf_cancel (inp); xfree (cfx.dek); @@ -757,15 +758,15 @@ write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in - * PROVIDED_PKS; if not the function builds a list of keys on its own. + * PROVIDED_KEYS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the * not yet finished server.c. */ int -encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, +encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, - int outputfd) + gnupg_fd_t outputfd) { iobuf_t inp = NULL; iobuf_t out = NULL; @@ -783,7 +784,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, PK_LIST pk_list; int do_compress; - if (filefd != -1 && filename) + if (filefd != GNUPG_INVALID_FD && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ do_compress = !!opt.compress_algo; @@ -814,7 +815,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, /* Prepare iobufs. */ #ifdef HAVE_W32_SYSTEM - if (filefd == -1) + if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else { @@ -822,7 +823,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, gpg_err_set_errno (ENOSYS); } #else - if (filefd == -1) + if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else inp = iobuf_fdopen_nc (filefd, "rb"); @@ -840,8 +841,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, char xname[64]; rc = gpg_error_from_syserror (); - if (filefd != -1) - snprintf (xname, sizeof xname, "[fd %d]", filefd); + if (filefd != GNUPG_INVALID_FD) + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (filefd)); else if (!filename) strcpy (xname, "[stdin]"); else @@ -1121,6 +1122,7 @@ write_pubkey_enc (ctrl_t ctrl, enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = throw_keyid; + enc->seskey_algo = dek->algo; /* (Used only by PUBKEY_ALGO_KYBER.) */ /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an @@ -1136,7 +1138,7 @@ write_pubkey_enc (ctrl_t ctrl, * build_packet(). */ frame = encode_session_key (pk->pubkey_algo, dek, pubkey_nbits (pk->pubkey_algo, pk->pkey)); - rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey); + rc = pk_encrypt (pk, frame, dek->algo, enc->data); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); @@ -1224,7 +1226,8 @@ encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); - rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1); + rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, line, remusr, + 0, NULL, GNUPG_INVALID_FD); if (rc) log_error ("encryption of '%s' failed: %s\n", print_fname_stdin(line), gpg_strerror (rc) ); @@ -1236,7 +1239,8 @@ encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); - if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, *files, remusr, + 0, NULL, GNUPG_INVALID_FD))) log_error("encryption of '%s' failed: %s\n", print_fname_stdin(*files), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); diff --git a/g10/export.c b/g10/export.c index 224847e0f..2417edef1 100644 --- a/g10/export.c +++ b/g10/export.c @@ -428,7 +428,7 @@ do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, memset( &zfx, 0, sizeof zfx); - rc = open_outfile (-1, NULL, 0, !!secret, &out ); + rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, !!secret, &out); if (rc) return rc; @@ -1961,6 +1961,11 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, err = 0; continue; } + if (strchr (hexgrip, ',')) + { + log_error ("exporting a secret dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } xfree (serialno); serialno = NULL; @@ -2707,18 +2712,18 @@ export_one_ssh_key (estream_t fp, PKT_public_key *pk) blob = get_membuf (&mb, &bloblen); if (blob) { - struct b64state b64_state; + gpgrt_b64state_t b64_state; es_fprintf (fp, "%s ", identifier); - err = b64enc_start_es (&b64_state, fp, ""); - if (err) + b64_state = gpgrt_b64enc_start (fp, ""); + if (!b64_state) { xfree (blob); goto leave; } - err = b64enc_write (&b64_state, blob, bloblen); - b64enc_finish (&b64_state); + err = gpgrt_b64enc_write (b64_state, blob, bloblen); + gpgrt_b64enc_finish (b64_state); es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL)); xfree (blob); @@ -2962,7 +2967,7 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) int pkalgo; int i; gcry_mpi_t keyparam[10] = { NULL }; - struct b64state b64_state; + gpgrt_b64state_t b64_state; init_membuf_secure (&mb, 1024); init_membuf_secure (&mb2, 1024); @@ -3140,11 +3145,11 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) goto leave; } - err = b64enc_start_es (&b64_state, fp, "OPENSSH PRIVATE_KEY"); - if (err) + b64_state = gpgrt_b64enc_start (fp, "OPENSSH PRIVATE_KEY"); + if (!b64_state) goto leave; - err = b64enc_write (&b64_state, blob, bloblen); - b64enc_finish (&b64_state); + err = gpgrt_b64enc_write (b64_state, blob, bloblen); + gpgrt_b64enc_finish (b64_state); if (err) goto leave; diff --git a/g10/filter.h b/g10/filter.h index 4b4fc55ff..321b553dc 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -29,6 +29,9 @@ typedef struct { size_t maxbuf_size; } md_filter_context_t; +typedef struct md_thd_filter_context *md_thd_filter_context_t; +void md_thd_filter_set_md (md_thd_filter_context_t mfx, gcry_md_hd_t md); + typedef struct { int refcount; /* Initialized to 1. */ @@ -165,6 +168,7 @@ typedef struct { /*-- mdfilter.c --*/ int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); +int md_thd_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); void free_md_filter_context( md_filter_context_t *mfx ); /*-- armor.c --*/ diff --git a/g10/getkey.c b/g10/getkey.c index ce59628a0..f2d1e7d7b 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -3779,6 +3779,16 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, continue; } + if (opt.flags.require_pqc_encryption + && (req_usage & PUBKEY_USAGE_ENC) + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey is not quantum-resistant\n"); + continue; + } + + if (want_secret) { int secret_key_avail = agent_probe_secret_key (NULL, pk); @@ -3857,6 +3867,13 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } + else if (opt.flags.require_pqc_encryption + && (req_usage & PUBKEY_USAGE_ENC) + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + { + if (DBG_LOOKUP) + log_debug ("\tprimary key is not quantum-resistant\n"); + } else /* Okay. */ { if (DBG_LOOKUP) @@ -207,6 +207,7 @@ enum cmd_and_opt_values oWithV5Fingerprint, oWithFingerprint, oWithSubkeyFingerprint, + oWithoutSubkeyFingerprint, oWithICAOSpelling, oWithKeygrip, oWithKeyScreening, @@ -355,6 +356,7 @@ enum cmd_and_opt_values oAllowSecretKeyImport, oAllowOldCipherAlgos, oEnableSpecialFilenames, + oDisableFdTranslation, oNoLiteral, oSetFilesize, oHonorHttpProxy, @@ -454,6 +456,7 @@ enum cmd_and_opt_values oAssertSigner, oAssertPubkeyAlgo, oKbxBufferSize, + oRequirePQCEncryption, oNoop }; @@ -825,6 +828,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"), + ARGPARSE_s_n (oWithoutSubkeyFingerprint, "without-subkey-fingerprint", "@"), ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), @@ -882,7 +886,6 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oAllowOldCipherAlgos, "allow-old-cipher-algos", "@"), ARGPARSE_s_s (oWeakDigest, "weak-digest","@"), ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"), - ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), @@ -894,7 +897,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), - + ARGPARSE_s_n (oRequirePQCEncryption, "require-pqc-encryption", "@"), ARGPARSE_header (NULL, N_("Options for unattended use")), @@ -914,6 +917,8 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), ARGPARSE_s_n (oForceSignKey, "force-sign-key", "@"), + ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), + ARGPARSE_s_n (oDisableFdTranslation, "disable-fd-translation", "@"), ARGPARSE_header (NULL, N_("Other options")), @@ -1033,6 +1038,8 @@ static struct debug_flags_s debug_flags [] = /* The list of compatibility flags. */ static struct compatibility_flags_s compatibility_flags [] = { + { COMPAT_PARALLELIZED, "parallelized" }, + { COMPAT_T7014_OLD, "t7014-old" }, { 0, NULL } }; @@ -1087,10 +1094,6 @@ static void read_sessionkey_from_fd (int fd); -/* NPth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - - static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { @@ -2091,6 +2094,8 @@ parse_list_options(char *str) NULL}, {"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL, N_("show user-supplied notations during signature listings")}, + {"show-x509-notations",LIST_SHOW_X509_NOTATIONS,NULL, NULL }, + {"store-x509-notations",LIST_STORE_X509_NOTATIONS,NULL, NULL }, {"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL, N_("show preferred keyserver URLs during signature listings")}, {"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL, @@ -2505,6 +2510,7 @@ main (int argc, char **argv) opt.passphrase_repeat = 1; opt.emit_version = 0; opt.weak_digests = NULL; + opt.with_subkey_fingerprint = 1; opt.compliance = CO_GNUPG; /* Check special options given on the command line. */ @@ -2912,6 +2918,9 @@ main (int argc, char **argv) case oWithSubkeyFingerprint: opt.with_subkey_fingerprint = 1; break; + case oWithoutSubkeyFingerprint: + opt.with_subkey_fingerprint = 0; + break; case oWithICAOSpelling: opt.with_icao_spelling = 1; break; @@ -3063,6 +3072,9 @@ main (int argc, char **argv) break; case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break; + case oRequirePQCEncryption: + opt.flags.require_pqc_encryption = 1; + break; case oRFC2440Text: opt.rfc2440_text=1; break; case oNoRFC2440Text: opt.rfc2440_text=0; break; @@ -3569,6 +3581,10 @@ main (int argc, char **argv) enable_special_filenames (); break; + case oDisableFdTranslation: + disable_translate_sys2libc_fd (); + break; + case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break; case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break; case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break; @@ -3907,8 +3923,8 @@ main (int argc, char **argv) /* Init threading which is used by some helper functions. */ npth_init (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); if (logfile) { @@ -4453,7 +4469,8 @@ main (int argc, char **argv) { if( argc > 1 ) wrong_args("--encrypt [filename]"); - if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, fname, remusr, + 0, NULL, GNUPG_INVALID_FD))) { write_status_failure ("encrypt", rc); log_error("%s: encryption failed: %s\n", @@ -4478,7 +4495,8 @@ main (int argc, char **argv) gnupg_compliance_option_string (opt.compliance)); else { - if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, fname, remusr, + 1, NULL, GNUPG_INVALID_FD))) { write_status_failure ("encrypt", rc); log_error ("%s: encryption failed: %s\n", @@ -5676,13 +5694,13 @@ print_mds( const char *fname, int algo ) } else { - fp = es_fopen (fname, "rb" ); - if (fp && is_secured_file (es_fileno (fp))) + if (is_secured_filename (fname)) { - es_fclose (fp); fp = NULL; gpg_err_set_errno (EPERM); } + else + fp = es_fopen (fname, "rb" ); } if (!fp) { @@ -41,6 +41,10 @@ /* Number of bits we accept when reading or writing MPIs. */ #define MAX_EXTERN_MPI_BITS 16384 +/* Number of bytes we accept when reading four-octet count prefixed + * key parameters. Needs to fit as a positive number into an int. */ +#define MAX_EXTERN_KEYPARM_BITS (32768*8) + /* The maximum length of a binary fingerprints. This is used to * provide a static buffer and will be increased if we need to support * longer fingerprints. Warning: At some places we have some diff --git a/g10/import.c b/g10/import.c index ff8847cb6..bbeeebdfe 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2175,10 +2175,12 @@ import_one_real (ctrl_t ctrl, merge_keys_done = 1; /* Note that we do not want to show the validity because the key * has not yet imported. */ - list_keyblock_direct (ctrl, keyblock, from_sk, 0, + err = list_keyblock_direct (ctrl, keyblock, from_sk, 0, opt.fingerprint || opt.with_fingerprint, 1); es_fflush (es_stdout); no_usable_encr_subkeys_warning (keyblock); + if (err) + goto leave; } /* Write the keyblock to the output and do not actually import. */ diff --git a/g10/keydb.h b/g10/keydb.h index 62a99295d..75a8ded72 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -578,7 +578,8 @@ char *hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen); char *v5hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen); char *format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen); -gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array); +gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array, + int get_second); gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); char *ecdh_param_str_from_pk (PKT_public_key *pk); diff --git a/g10/keyedit.c b/g10/keyedit.c index f4f59de03..81ea06c24 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1103,6 +1103,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock) err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; + /* FIXME: Handle dual keys. */ err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); if (!err && serialno) ; /* Key on card. */ diff --git a/g10/keygen.c b/g10/keygen.c index 48e5b3a40..4bdb9f53a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,7 +1,7 @@ /* keygen.c - Generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2015, 2016, 2017, 2018 Werner Koch - * Copyright (C) 2020 g10 Code GmbH + * Copyright (C) 2020, 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -139,10 +139,23 @@ struct common_gen_cb_parm_s * may take a copy of this so that the result can be used after we * are back from the deep key generation call stack. */ gcry_sexp_t genkey_result; + /* For a dual algorithms the result of the second algorithm + * (e.g. Kyber). */ + gcry_sexp_t genkey_result2; }; typedef struct common_gen_cb_parm_s *common_gen_cb_parm_t; +/* A communication object to help adding certain notations to a key + * binding signature. */ +struct opaque_data_usage_and_pk +{ + unsigned int usage; + const char *cpl_notation; + PKT_public_key *pk; +}; + + /* FIXME: These globals vars are ugly. And using MAX_PREFS even for * aeads is useless, given that we don't expects more than a very few * algorithms. */ @@ -174,6 +187,9 @@ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, u32 expireval, int *keygen_flags); static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max); +static void do_add_notation (PKT_signature *sig, + const char *name, const char *value, + int critical); @@ -338,6 +354,20 @@ keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) } +/* This is only used to write the key binding signature. It is not + * used for the primary key. */ +static int +keygen_add_key_flags_from_oduap (PKT_signature *sig, void *opaque) +{ + struct opaque_data_usage_and_pk *oduap = opaque; + + do_add_key_flags (sig, oduap->usage); + if (oduap->cpl_notation) + do_add_notation (sig, "[email protected]", oduap->cpl_notation, 0); + return keygen_add_key_expire (sig, oduap->pk); +} + + static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { @@ -943,6 +973,44 @@ keygen_add_keyserver_url(PKT_signature *sig, void *opaque) return 0; } + +/* This function is used to add a notations to a signature. In + * general the caller should have cleared exiting notations before + * adding new ones. For example by calling: + * + * delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); + * delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); + * + * Only human readable notaions may be added. NAME and value are + * expected to be UTF-* strings. + */ +static void +do_add_notation (PKT_signature *sig, const char *name, const char *value, + int critical) +{ + unsigned char *buf; + unsigned int n1,n2; + + n1 = strlen (name); + n2 = strlen (value); + + buf = xmalloc (8 + n1 + n2); + + buf[0] = 0x80; /* human readable. */ + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy (buf+8, name, n1); + memcpy (buf+8+n1, value, n2); + build_sig_subpkt (sig, + (SIGSUBPKT_NOTATION|(critical?SIGSUBPKT_FLAG_CRITICAL:0)), + buf, 8+n1+n2 ); + xfree (buf); +} + + int keygen_add_notations(PKT_signature *sig,void *opaque) { @@ -992,6 +1060,7 @@ keygen_add_notations(PKT_signature *sig,void *opaque) return 0; } + int keygen_add_revkey (PKT_signature *sig, void *opaque) { @@ -1225,6 +1294,7 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; + struct opaque_data_usage_and_pk oduap; if (opt.verbose) log_info(_("writing key binding signature\n")); @@ -1250,10 +1320,21 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, BUG(); /* Make the signature. */ - sub_pk->pubkey_usage = use; + oduap.usage = use; + if ((use & PUBKEY_USAGE_ENC) + && opt.compliance == CO_DE_VS + /* The required libgcrypt 1.11 won't yet claim a compliant RNG. */ + && gnupg_rng_is_compliant (CO_DE_VS)) + oduap.cpl_notation = "de-vs"; + else if ((use & PUBKEY_USAGE_ENC) + && sub_pk->pubkey_algo == PUBKEY_ALGO_KYBER) + oduap.cpl_notation = "fips203.ipd.2023-08-24"; + else + oduap.cpl_notation = NULL; + oduap.pk = sub_pk; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, timestamp, 0, - keygen_add_key_flags_and_expire, sub_pk, + keygen_add_key_flags_from_oduap, &oduap, cache_nonce); if (err) { @@ -1311,8 +1392,12 @@ curve_is_448 (gcry_sexp_t sexp) } +/* Extract the parameters in OpenPGP format from SEXP and put them + * into the caller provided ARRAY. SEXP2 is used to provide the + * parameters for dual algorithm (e.g. Kyber). */ static gpg_error_t -ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) +ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, + gcry_sexp_t sexp2, int algo, int pkversion) { gpg_error_t err; gcry_sexp_t list, l2; @@ -1355,6 +1440,10 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } + /* For v5 keys we prefer the modern OID for cv25519. */ + if (pkversion > 4 && !strcmp (oidstr, "1.3.6.1.4.1.3029.1.5.1")) + oidstr = "1.3.101.110"; + err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; @@ -1364,8 +1453,46 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) goto leave; gcry_sexp_release (list); + list = NULL; - if (algo == PUBKEY_ALGO_ECDH) + if (algo == PUBKEY_ALGO_KYBER) + { + if (!sexp2) + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + + list = gcry_sexp_find_token (sexp2, "public-key", 0); + if (!list) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + + l2 = gcry_sexp_find_token (list, "p", 1); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ + goto leave; + } + array[2] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); + gcry_sexp_release (l2); + if (!array[2]) + { + err = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ + goto leave; + } + } + else if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) @@ -1377,6 +1504,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) leave: xfree (curve); + gcry_sexp_release (list); if (err) { for (i=0; i < 3; i++) @@ -1455,10 +1583,24 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + gcry_sexp_t s_key2 = NULL; const char *algoelem; + char *hexkeygrip_buffer = NULL; + char *hexkeygrip2 = NULL; if (hexkeygrip[0] == '&') hexkeygrip++; + if (strchr (hexkeygrip, ',')) + { + hexkeygrip_buffer = xtrystrdup (hexkeygrip); + if (!hexkeygrip_buffer) + return gpg_error_from_syserror (); + hexkeygrip = hexkeygrip_buffer; + hexkeygrip2 = strchr (hexkeygrip_buffer, ','); + if (hexkeygrip2) + *hexkeygrip2++ = 0; + } + switch (algo) { @@ -1468,16 +1610,21 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: algoelem = ""; break; case PUBKEY_ALGO_EDDSA: algoelem = ""; break; - default: return gpg_error (GPG_ERR_INTERNAL); + case PUBKEY_ALGO_KYBER: algoelem = ""; break; + default: + xfree (hexkeygrip_buffer); + return gpg_error (GPG_ERR_INTERNAL); } - /* Ask the agent for the public key matching HEXKEYGRIP. */ if (cardkey) { err = agent_scd_readkey (ctrl, hexkeygrip, &s_key, NULL); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } } else { @@ -1485,16 +1632,41 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, err = agent_readkey (ctrl, 0, hexkeygrip, &public); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } err = gcry_sexp_sscan (&s_key, NULL, public, gcry_sexp_canon_len (public, 0, NULL, NULL)); xfree (public); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } + if (hexkeygrip2) + { + err = agent_readkey (ctrl, 0, hexkeygrip2, &public); + if (err) + { + gcry_sexp_release (s_key); + xfree (hexkeygrip_buffer); + return err; + } + err = gcry_sexp_sscan (&s_key2, NULL, public, + gcry_sexp_canon_len (public, 0, NULL, NULL)); + xfree (public); + if (err) + { + gcry_sexp_release (s_key); + xfree (hexkeygrip_buffer); + return err; + } + } } - /* For X448 we force the use of v5 packets. */ - if (curve_is_448 (s_key)) + /* For X448 and Kyber we force the use of v5 packets. */ + if (curve_is_448 (s_key) || algo == PUBKEY_ALGO_KYBER) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; /* Build a public key packet. */ @@ -1503,6 +1675,8 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); + xfree (hexkeygrip_buffer); return err; } @@ -1512,26 +1686,32 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); + else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } @@ -1539,16 +1719,18 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); + xfree (hexkeygrip_buffer); return 0; } -/* Common code for the key generation function gen_xxx. The optinal +/* Common code for the key generation function gen_xxx. The optional * (COMMON_GEN_CB,COMMON_GEN_CB_PARM) can be used as communication - * object. + * object. A KEYPARMS2 forces the use of a dual key (e.g. Kyber+ECC). */ static int -common_gen (const char *keyparms, int algo, const char *algoelem, +common_gen (const char *keyparms, const char *keyparms2, + int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, @@ -1559,6 +1741,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + gcry_sexp_t s_key2 = NULL; err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), @@ -1570,14 +1753,32 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } + if (keyparms2) + { + err = agent_genkey (NULL, NULL, NULL, keyparms2, + 1 /* No protection */, + NULL, timestamp, + &s_key2); + if (err) + { + log_error ("agent_genkey failed for second algo: %s\n", + gpg_strerror (err) ); + gcry_sexp_release (s_key); + return err; + } + } + if (common_gen_cb && common_gen_cb_parm) { common_gen_cb_parm->genkey_result = s_key; + common_gen_cb_parm->genkey_result2 = s_key2; err = common_gen_cb (common_gen_cb_parm); common_gen_cb_parm->genkey_result = NULL; + common_gen_cb_parm->genkey_result2 = NULL; if (err) { gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); return err; } } @@ -1596,10 +1797,12 @@ common_gen (const char *keyparms, int algo, const char *algoelem, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA - || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -1610,6 +1813,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) @@ -1675,7 +1879,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "pgy", + err = common_gen (keyparms, NULL, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1767,7 +1971,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", + err = common_gen (keyparms, NULL, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1868,7 +2072,7 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "", + err = common_gen (keyparms, NULL, algo, "", pub_root, timestamp, expireval, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1880,6 +2084,79 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, } +/* Generate a dual ECC+Kyber key. Note that KEYGEN_FLAGS will be + * updated by this function to indicate the forced creation of a v5 + * key. */ +static gpg_error_t +gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int *keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr, + gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), + common_gen_cb_parm_t common_gen_cb_parm) +{ + gpg_error_t err; + char *keyparms1; + const char *keyparms2; + + log_assert (algo == PUBKEY_ALGO_KYBER); + + if (nbits == 768) + keyparms2 = "(genkey(kyber768))"; + else if (nbits == 1024) + keyparms2 = "(genkey(kyber1024))"; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + if (!curve || !*curve) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + + *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + + if (!strcmp (curve, "Curve25519")) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (!strcmp (curve, "X448")) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else /* Should we use the compressed format? Check smartcard support. */ + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + + if (!keyparms1) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms1, keyparms2, algo, "", + pub_root, timestamp, expireval, is_subkey, + *keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); + xfree (keyparms1); + } + + return err; +} + + /* * Generate an RSA key. */ @@ -1928,7 +2205,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "ne", + err = common_gen (keyparms, NULL, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -2350,7 +2627,26 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, continue; } - if (strlen (answer) != 40 && + if (strlen (answer) == 40+1+40 && answer[40]==',') + { + int algo1, algo2; + + answer[40] = 0; + algo1 = check_keygrip (ctrl, answer); + algo2 = check_keygrip (ctrl, answer+41); + answer[40] = ','; + if (algo1 == PUBKEY_ALGO_ECDH && algo2 == PUBKEY_ALGO_KYBER) + { + algo = PUBKEY_ALGO_KYBER; + break; + } + else if (!algo1 || !algo2) + tty_printf (_("No key with this keygrip\n")); + else + tty_printf ("Invalid combination for dual algo (%d,%d)\n", + algo1, algo2); + } + else if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); @@ -2560,6 +2856,12 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) def=255; break; + case PUBKEY_ALGO_KYBER: + *min = 768; + *max = 1024; + def = 768; + break; + default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; @@ -2575,45 +2877,44 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) static unsigned int fixup_keysize (unsigned int nbits, int algo, int silent) { + unsigned int orig_nbits = nbits; + if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits); } else if (algo == PUBKEY_ALGO_EDDSA) { - if (nbits != 255 && nbits != 441) - { - if (nbits < 256) - nbits = 255; - else - nbits = 441; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 255; + else + nbits = 441; } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { - if (nbits != 256 && nbits != 384 && nbits != 521) - { - if (nbits < 256) - nbits = 256; - else if (nbits < 384) - nbits = 384; - else - nbits = 521; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 256; + else if (nbits < 384) + nbits = 384; + else + nbits = 521; + } + else if (algo == PUBKEY_ALGO_KYBER) + { + /* (in reality the numbers are not bits) */ + if (nbits < 768) + nbits = 768; + else if (nbits > 1024) + nbits = 1024; } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits ); } + if (!silent && orig_nbits != nbits) + tty_printf (_("rounded to %u bits\n"), nbits); + return nbits; } @@ -3343,6 +3644,12 @@ do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); + else if (algo == PUBKEY_ALGO_KYBER) + err = gen_kyber (algo, nbits, curve, + pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, @@ -3421,6 +3728,7 @@ parse_key_parameter_part (ctrl_t ctrl, char *keygrip = NULL; u32 keytime = 0; int is_448 = 0; + int is_pqc = 0; if (!string || !*string) return 0; /* Success. */ @@ -3446,6 +3754,7 @@ parse_key_parameter_part (ctrl_t ctrl, ; /* We need the flags before we can figure out the key to use. */ else if (algo) { + /* This is one of the algos parsed above (rsa, dsa, or elg). */ if (!string[3]) size = get_keysize_range (algo, NULL, NULL); else @@ -3455,6 +3764,63 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_INV_VALUE); } } + else if (!ascii_strcasecmp (string, "kyber") + || !ascii_strcasecmp (string, "kyber768")) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP256r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 768; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "kyber1024")) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP384r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 1024; + is_pqc = 1; + } + else if (!ascii_strncasecmp (string, "ky768_", 6) + || !ascii_strncasecmp (string, "ky1024_", 7) + || !ascii_strncasecmp (string, "kyber768_", 9) + || !ascii_strncasecmp (string, "kyber1024_", 10) + ) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + s = strchr (string, '_'); + log_assert (s); + s++; + curve = openpgp_is_curve_supported (s, &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = strstr (string, "768_")? 768 : 1024; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "dil3")) + { + algo = PUBKEY_ALGO_DIL3_25519; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "dil5")) + { + algo = PUBKEY_ALGO_DIL5_448; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "sphinx") + || !ascii_strcasecmp (string, "sphinx_sha2")) + { + algo = PUBKEY_ALGO_SPHINX_SHA2; + is_pqc = 1; + } else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) { if (!algo) @@ -3703,8 +4069,8 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } - /* Ed448 and X448 must only be used as v5 keys. */ - if (is_448) + /* Ed448, X448 and the PQC algos must only be used as v5 keys. */ + if (is_448 || is_pqc) { if (keyversion == 4) log_info (_("WARNING: v4 is specified, but overridden by v5.\n")); @@ -3768,6 +4134,8 @@ parse_key_parameter_part (ctrl_t ctrl, * cv25519 := ECDH using curve Curve25519. * cv448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 + * kyber := Kyber with the default parameters + * ky768_bp384 := Kyber-768 with BrainpoolP256r1 as second algo * * All strings with an unknown prefix are considered an elliptic * curve. Curves which have no implicit algorithm require that FLAGS @@ -5924,9 +6292,12 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, list_keyblock_direct (ctrl, pub_root, 0, 1, opt.fingerprint || opt.with_fingerprint, 1); + /* Note that we ignore errors from the list function + * because that would only be an additional info. It + * has already been remarked that the key has been + * created. */ } - if (!opt.batch && (get_parameter_algo (ctrl, para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA @@ -6137,6 +6508,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; + /* FIXME: Right now the primary key won't be a dual key. But this + * will change */ if (agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) { if (interactive) @@ -6476,12 +6849,14 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, if (curve_is_448 (s_key)) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; + if (algo == PUBKEY_ALGO_RSA) err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); @@ -6494,7 +6869,6 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, } pk->timestamp = *timestamp; - pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; diff --git a/g10/keyid.c b/g10/keyid.c index fab1e3a36..08f684829 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -74,12 +74,18 @@ pubkey_letter( int algo ) is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: - "rsa3072" - RSA with 3072 bit - "elg1024" - Elgamal with 1024 bit - "ed25519" - ECC using the curve Ed25519. - "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". + "rsa3072" - RSA with 3072 bit + "elg1024" - Elgamal with 1024 bit + "ed25519" - EdDSA using the curve Ed25519. + "cv25519" - ECDH using the curve X25519. + "ky768_cv448 - Kyber-768 with X448 as second algo. + "ky1025_bp512 - Kyber-1024 with BrainpoolP256r1 as second algo. + "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". + "unknown_N" - Unknown OpenPGP algorithm N. "E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID. - "unknown_N" - Unknown OpenPGP algorithm N. + + Note that with Kyber we use "bp" as abbreviation for BrainpoolP and + ignore the final r1 part. If the option --legacy-list-mode is active, the output use the legacy format: @@ -97,6 +103,9 @@ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; + int dual = 0; + char *curve; + const char *name; if (opt.legacy_list_mode) { @@ -116,14 +125,34 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: prefix = ""; break; + case PUBKEY_ALGO_KYBER: prefix = "ky"; dual = 1; break; + case PUBKEY_ALGO_DIL3_25519: prefix = "dil3"; break; + case PUBKEY_ALGO_DIL5_448: prefix = "dil5"; break; + case PUBKEY_ALGO_SPHINX_SHA2: prefix = "sphinx_sha2"; break; } + if (prefix && *prefix) - snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); + { + if (dual) + { + curve = openpgp_oid_to_str (pk->pkey[0]); + /* Note that we prefer the abbreviated name of the curve. */ + name = openpgp_oid_to_curve (curve, 2); + if (!name) + name = "unknown"; + + snprintf (buffer, bufsize, "%s%u_%s", + prefix, nbits_from_pk (pk), name); + xfree (curve); + } + else + snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); + } else if (prefix) { - char *curve = openpgp_oid_to_str (pk->pkey[0]); - const char *name = openpgp_oid_to_curve (curve, 0); + curve = openpgp_oid_to_str (pk->pkey[0]); + name = openpgp_oid_to_curve (curve, 0); if (name) snprintf (buffer, bufsize, "%s", name); @@ -303,6 +332,30 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) pp[i] = NULL; nn[i] = 0; } + else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + { + /* Ugly: We need to re-construct the wire format of the + * key parameter. It would be easier to use a second + * index for pp and nn which we could bump independet of + * i. */ + const char *p; + + p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); + nn[i] = (nbits+7)/8; + pp[i] = xmalloc (4 + nn[i] + 1); + if (p) + { + pp[i][0] = nn[i] >> 24; + pp[i][1] = nn[i] >> 16; + pp[i][2] = nn[i] >> 8; + pp[i][3] = nn[i]; + memcpy (pp[i] + 4 , p, nn[i]); + nn[i] += 4; + } + else + pp[i] = NULL; + n += nn[i]; + } else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const char *p; @@ -800,11 +853,28 @@ namehash_from_uid (PKT_user_id *uid) /* - * Return the number of bits used in PK. + * Return the number of bits used in PK. For Kyber we return the + * octet count of the Kyber part and not of the ECC (thus likely + * values are 768 or 1024). */ unsigned int nbits_from_pk (PKT_public_key *pk) { + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + unsigned int nbits; + if (!gcry_mpi_get_opaque (pk->pkey[2], &nbits)) + return 0; + switch (nbits/8) + { + case 800: nbits = 512; break; + case 1184: nbits = 768; break; + case 1568: nbits = 1024; break; + default: nbits = 0; break; /* Unkown version. */ + } + return nbits; + } + else return pubkey_nbits (pk->pubkey_algo, pk->pkey); } @@ -1230,16 +1300,22 @@ format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) /* Return the so called KEYGRIP which is the SHA-1 hash of the public - key parameters expressed as an canonical encoded S-Exp. ARRAY must - be 20 bytes long. Returns 0 on success or an error code. */ + * key parameters expressed as an canonical encoded S-Exp. ARRAY must + * be 20 bytes long. Returns 0 on success or an error code. If + * GET_SECOND Is one and PK has dual algorithm, the keygrip of the + * second algorithm is return; GPG_ERR_FALSE is returned if the algo + * is not a dual algorithm. */ gpg_error_t -keygrip_from_pk (PKT_public_key *pk, unsigned char *array) +keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) { gpg_error_t err; gcry_sexp_t s_pkey; if (DBG_PACKET) - log_debug ("get_keygrip for public key\n"); + log_debug ("get_keygrip for public key%s\n", get_second?" (second)":""); + + if (get_second && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + return gpg_error (GPG_ERR_FALSE); switch (pk->pubkey_algo) { @@ -1287,6 +1363,33 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) } break; + case PUBKEY_ALGO_KYBER: + if (get_second) + { + char tmpname[15]; + + snprintf (tmpname, sizeof tmpname, "kyber%u", nbits_from_pk (pk)); + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(%s(p%m)))", + tmpname, pk->pkey[2]); + } + else + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + if (!curve) + err = gpg_error_from_syserror (); + else + { + err = gcry_sexp_build (&s_pkey, NULL, + openpgp_oid_is_cv25519 (pk->pkey[0]) + ? "(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))" + : "(public-key(ecc(curve%s)(q%m)))", + curve, pk->pkey[1]); + xfree (curve); + } + } + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -1319,26 +1422,45 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) /* Store an allocated buffer with the keygrip of PK encoded as a - hexstring at r_GRIP. Returns 0 on success. */ + * hexstring at r_GRIP. Returns 0 on success. For dual algorithms + * the keygrips are delimited by a comma. */ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; + char *buf; unsigned char grip[KEYGRIP_LEN]; + unsigned char grip2[KEYGRIP_LEN]; *r_grip = NULL; - err = keygrip_from_pk (pk, grip); + err = keygrip_from_pk (pk, grip, 0); if (!err) { - char * buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); - if (!buf) - err = gpg_error_from_syserror (); + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + err = keygrip_from_pk (pk, grip2, 1); + if (err) + goto leave; + buf = xtrymalloc (2 * KEYGRIP_LEN * 2 + 1 + 1); + } else + buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); + + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + bin2hex (grip, KEYGRIP_LEN, buf); + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) { - bin2hex (grip, KEYGRIP_LEN, buf); - *r_grip = buf; + buf[2*KEYGRIP_LEN] = ','; + bin2hex (grip2, KEYGRIP_LEN, buf+2*KEYGRIP_LEN+1); } + *r_grip = buf; } + leave: return err; } diff --git a/g10/keylist.c b/g10/keylist.c index cc1e23d7f..81d6805a5 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -82,7 +82,7 @@ static estream_t attrib_fp; -static void list_keyblock (ctrl_t ctrl, +static gpg_error_t list_keyblock (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx); @@ -745,6 +745,7 @@ list_all (ctrl_t ctrl, int secret, int mark_secret) int any_secret; const char *lastresname, *resname; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) @@ -802,13 +803,13 @@ list_all (ctrl_t ctrl, int secret, int mark_secret) } } merge_keys_and_selfsig (ctrl, keyblock); - list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, - &listctx); + listerr = list_keyblock (ctrl, keyblock, secret, any_secret, + opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } - while (!(rc = keydb_search_next (hd))); + while (!listerr && !(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); @@ -839,6 +840,7 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) @@ -887,12 +889,12 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } - list_keyblock (ctrl, keyblock, secret, any_secret, - opt.fingerprint, &listctx); + listerr = list_keyblock (ctrl, keyblock, secret, any_secret, + opt.fingerprint, &listctx); } release_kbnode (keyblock); } - while (!getkey_next (ctrl, ctx, NULL, &keyblock)); + while (!listerr && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); if (opt.check_sigs && !opt.with_colons) @@ -910,12 +912,13 @@ locate_one (ctrl_t ctrl, strlist_t names, int no_local) GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; - for (sl = names; sl; sl = sl->next) + for (sl = names; sl && !listerr; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, no_local? GET_PUBKEY_NO_LOCAL @@ -933,10 +936,11 @@ locate_one (ctrl_t ctrl, strlist_t names, int no_local) { do { - list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); + listerr = list_keyblock (ctrl, keyblock, 0, 0, + opt.fingerprint, &listctx); release_kbnode (keyblock); } - while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); + while (!listerr && ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); ctx = NULL; } @@ -1171,6 +1175,77 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) } +static void +print_keygrip (const char *keygrip) +{ + const char *s; + + s = strchr (keygrip, ','); + if (s) + es_fprintf (es_stdout, " Keygrip = %.*s,\n%*s%s\n", + (int)(s-keygrip), keygrip, 16, "", s+1); + else + es_fprintf (es_stdout, " Keygrip = %s\n", keygrip); +} + + +/* If PK is given the output is written to a new file instead of + * stdout. */ +static void +print_x509_notations (struct notation *nots, PKT_public_key *pk) +{ + gpg_error_t err; + gpgrt_b64state_t state = NULL; + char hexfpr[2*4 + 1 + 2*MAX_FINGERPRINT_LEN+4+1]; + char sha1[20]; + estream_t fp; + + for (; nots; nots = nots->next) + { + if (pk) + { + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1, nots->bdat, nots->blen); + bin2hex (sha1+16, 4, hexfpr); + hexfpr[2*4] = '-'; + hexfingerprint (pk, hexfpr + 2*4+1, 2*MAX_FINGERPRINT_LEN); + strcat (hexfpr, ".pem"); + fp = es_fopen (hexfpr, "w"); + if (!fp) + { + err = gpg_err_code_from_syserror (); + goto b64fail; + } + } + else + fp = es_stdout; + state = gpgrt_b64enc_start (fp, "CERTIFICATE"); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto b64fail; + } + err = gpgrt_b64enc_write (state, nots->bdat, nots->blen); + if (err) + goto b64fail; + err = gpgrt_b64enc_finish (state); + if (err) + goto b64fail; + if (fp != es_stdout) + { + es_fclose (fp); + fp = NULL; + } + } + return; + + b64fail: + log_error ("error writing base64 encoded notation: %s\n", gpg_strerror (err)); + gpgrt_b64enc_finish (state); + if (fp && fp != es_stdout) + gpgrt_fcancel (fp); +} + + /* Order two signatures. We first order by keyid and then by creation * time. */ int @@ -1220,170 +1295,186 @@ cmp_signodes (const void *av, const void *bv) * NODFLG_MARK_B to indicate self-signatures. */ static void list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, - struct keylist_context *listctx) + struct keylist_context *listctx, PKT_public_key *lastpk) { - /* (extra indentation to keep the diff history short) */ - PKT_signature *sig = node->pkt->pkt.signature; - int rc, sigrc; - char *sigstr; - char *reason_text = NULL; - char *reason_comment = NULL; - size_t reason_commentlen; - int reason_code = 0; + PKT_signature *sig = node->pkt->pkt.signature; + int rc, sigrc; + char *sigstr; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; + int reason_code = 0; + + if (listctx->check_sigs) + { + rc = check_key_signature (ctrl, keyblock, node, NULL); + switch (gpg_err_code (rc)) + { + case 0: + listctx->good_sigs++; + sigrc = '!'; + break; + case GPG_ERR_BAD_SIGNATURE: + listctx->inv_sigs++; + sigrc = '-'; + break; + case GPG_ERR_NO_PUBKEY: + case GPG_ERR_UNUSABLE_PUBKEY: + listctx->no_key++; + return; + case GPG_ERR_DIGEST_ALGO: + case GPG_ERR_PUBKEY_ALGO: + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)) + return; + /* fallthru. */ + default: + listctx->oth_err++; + sigrc = '%'; + break; + } - if (listctx->check_sigs) - { - rc = check_key_signature (ctrl, keyblock, node, NULL); - switch (gpg_err_code (rc)) - { - case 0: - listctx->good_sigs++; - sigrc = '!'; - break; - case GPG_ERR_BAD_SIGNATURE: - listctx->inv_sigs++; - sigrc = '-'; - break; - case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: - listctx->no_key++; - return; - case GPG_ERR_DIGEST_ALGO: - case GPG_ERR_PUBKEY_ALGO: - if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)) - return; - /* fallthru. */ - default: - listctx->oth_err++; - sigrc = '%'; - break; - } + /* TODO: Make sure a cached sig record here still has + the pk that issued it. See also + keyedit.c:print_and_check_one_sig */ + } + else + { + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS) + && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo) + == GPG_ERR_PUBKEY_ALGO) + || gpg_err_code (openpgp_md_test_algo (sig->digest_algo) + == GPG_ERR_DIGEST_ALGO) + || (sig->digest_algo == DIGEST_ALGO_SHA1 + && !(node->flag & NODFLG_MARK_B) /*no selfsig*/ + && !opt.flags.allow_weak_key_signatures))) + return; + rc = 0; + sigrc = ' '; + } - /* TODO: Make sure a cached sig record here still has - the pk that issued it. See also - keyedit.c:print_and_check_one_sig */ - } - else - { - if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS) - && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo) - == GPG_ERR_PUBKEY_ALGO) - || gpg_err_code (openpgp_md_test_algo (sig->digest_algo) - == GPG_ERR_DIGEST_ALGO) - || (sig->digest_algo == DIGEST_ALGO_SHA1 - && !(node->flag & NODFLG_MARK_B) /*no selfsig*/ - && !opt.flags.allow_weak_key_signatures))) - return; - rc = 0; - sigrc = ' '; - } + if (IS_KEY_REV (sig) || IS_SUBKEY_REV (sig) || IS_UID_REV (sig)) + { + sigstr = "rev"; + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, + &reason_commentlen); + } + else if (IS_UID_SIG (sig)) + sigstr = "sig"; + else if (IS_SUBKEY_SIG (sig)) + sigstr = "sig"; + else if (IS_KEY_SIG (sig)) + sigstr = "sig"; + else + { + es_fprintf (es_stdout, "sig " + "[unexpected signature class 0x%02x]\n", + sig->sig_class); + return; + } - if (sig->sig_class == 0x20 || sig->sig_class == 0x28 - || sig->sig_class == 0x30) - { - sigstr = "rev"; - reason_code = get_revocation_reason (sig, &reason_text, - &reason_comment, - &reason_commentlen); - } - else if ((sig->sig_class & ~3) == 0x10) - sigstr = "sig"; - else if (sig->sig_class == 0x18) - sigstr = "sig"; - else if (sig->sig_class == 0x1F) - sigstr = "sig"; - else - { - es_fprintf (es_stdout, "sig " - "[unexpected signature class 0x%02x]\n", - sig->sig_class); - return; - } + es_fputs (sigstr, es_stdout); + es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", + sigrc, (sig->sig_class - 0x10 > 0 && + sig->sig_class - 0x10 < + 4) ? '0' + sig->sig_class - 0x10 : ' ', + sig->flags.exportable ? ' ' : 'L', + sig->flags.revocable ? ' ' : 'R', + sig->flags.policy_url ? 'P' : ' ', + sig->flags.notation ? 'N' : ' ', + sig->flags.expired ? 'X' : ' ', + (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > + 0) ? '0' + + sig->trust_depth : ' ', keystr (sig->keyid), + datestr_from_sig (sig)); + if (opt.list_options & LIST_SHOW_SIG_EXPIRE) + es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); + es_fprintf (es_stdout, " "); + if (sigrc == '%') + es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); + else if (sigrc == '?') + ; + else if ((node->flag & NODFLG_MARK_B)) + es_fputs (_("[self-signature]"), es_stdout); + else if (!opt.fast_list_mode ) + { + size_t n; + char *p = get_user_id (ctrl, sig->keyid, &n, NULL); + print_utf8_buffer (es_stdout, p, n); + xfree (p); + } + es_putc ('\n', es_stdout); - es_fputs (sigstr, es_stdout); - es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", - sigrc, (sig->sig_class - 0x10 > 0 && - sig->sig_class - 0x10 < - 4) ? '0' + sig->sig_class - 0x10 : ' ', - sig->flags.exportable ? ' ' : 'L', - sig->flags.revocable ? ' ' : 'R', - sig->flags.policy_url ? 'P' : ' ', - sig->flags.notation ? 'N' : ' ', - sig->flags.expired ? 'X' : ' ', - (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > - 0) ? '0' + - sig->trust_depth : ' ', keystr (sig->keyid), - datestr_from_sig (sig)); - if (opt.list_options & LIST_SHOW_SIG_EXPIRE) - es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); - es_fprintf (es_stdout, " "); - if (sigrc == '%') - es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); - else if (sigrc == '?') - ; - else if ((node->flag & NODFLG_MARK_B)) - es_fputs (_("[self-signature]"), es_stdout); - else if (!opt.fast_list_mode ) - { - size_t n; - char *p = get_user_id (ctrl, sig->keyid, &n, NULL); - print_utf8_buffer (es_stdout, p, n); - xfree (p); - } - es_putc ('\n', es_stdout); + if (sig->flags.policy_url + && (opt.list_options & LIST_SHOW_POLICY_URLS)) + show_policy_url (sig, 3, 0); + + if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) + show_notation (sig, 3, 0, + ((opt. + list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + + + ((opt. + list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : + 0)); + + if (sig->flags.notation + && (opt.list_options + & (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS))) + { + struct notation *nots; - if (sig->flags.policy_url - && (opt.list_options & LIST_SHOW_POLICY_URLS)) - show_policy_url (sig, 3, 0); + if ((IS_KEY_SIG (sig) || IS_SUBKEY_SIG (sig)) + && (nots = search_sig_notations (sig, + "[email protected]"))) + { + if ((opt.list_options & LIST_STORE_X509_NOTATIONS)) + print_x509_notations (nots, lastpk); + else + print_x509_notations (nots, NULL); + free_notation (nots); + } + } - if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) - show_notation (sig, 3, 0, - ((opt. - list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) - + - ((opt. - list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : - 0)); + if (sig->flags.pref_ks + && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) + show_keyserver_url (sig, 3, 0); - if (sig->flags.pref_ks - && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) - show_keyserver_url (sig, 3, 0); + if (reason_text && (reason_code || reason_comment)) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; - if (reason_text && (reason_code || reason_comment)) + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do { - es_fprintf (es_stdout, " %s%s\n", - _("reason for revocation: "), reason_text); - if (reason_comment) + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) { - const byte *s, *s_lf; - size_t n, n_lf; - - s = reason_comment; - n = reason_commentlen; - s_lf = NULL; - do - { - /* We don't want any empty lines, so we skip them. */ - for (;n && *s == '\n'; s++, n--) - ; - if (n) - { - s_lf = memchr (s, '\n', n); - n_lf = s_lf? s_lf - s : n; - es_fprintf (es_stdout, " %s", - _("revocation comment: ")); - es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); - es_putc ('\n', es_stdout); - s += n_lf; n -= n_lf; - } - } while (s_lf); + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; } - } + } while (s_lf); + } + } - xfree (reason_text); - xfree (reason_comment); + xfree (reason_text); + xfree (reason_comment); - /* fixme: check or list other sigs here */ + /* fixme: check or list other sigs here */ } @@ -1394,6 +1485,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, int rc; kbnode_t node; PKT_public_key *pk; + PKT_public_key *lastpk; u32 *mainkid; int skip_sigs = 0; char *hexgrip = NULL; @@ -1410,6 +1502,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, pk = node->pkt->pkt.public_key; mainkid = pk_keyid (pk); + lastpk = pk; if (secret || opt.with_keygrip) { @@ -1437,7 +1530,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_fingerprint (ctrl, NULL, pk, 0); if (opt.with_keygrip && hexgrip) - es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); + print_keygrip (hexgrip); if (serialno) print_card_serialno (serialno); @@ -1558,6 +1651,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, { PKT_public_key *pk2 = node->pkt->pkt.public_key; + lastpk = pk2; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { @@ -1593,13 +1687,15 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) - es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); + print_keygrip (hexgrip); if (opt.with_key_data) print_key_data (pk2); if (opt.with_key_screening) print_pk_screening (pk2, 0); } - else if (opt.list_sigs + else if ((opt.list_sigs + || (opt.list_options + & (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS))) && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { kbnode_t n; @@ -1627,7 +1723,8 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes); for (idx=0; idx < sigcount; idx++) - list_signature_print (ctrl, keyblock, sigarray[idx], listctx); + list_signature_print (ctrl, keyblock, sigarray[idx], listctx, + lastpk); xfree (sigarray); } } @@ -2232,11 +2329,18 @@ reorder_keyblock (KBNODE keyblock) } -static void +/* Note: If this function returns an error the caller is expected to + * honor this and stop all further processing. Any error returned + * will be a write error (to stdout) and a diagnostics is always + * printed using log_error. */ +static gpg_error_t list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { + gpg_error_t err = 0; + + es_clearerr (es_stdout); reorder_keyblock (keyblock); if (list_filter.selkey) @@ -2254,7 +2358,7 @@ list_keyblock (ctrl_t ctrl, } } if (!selected) - return; /* Skip this one. */ + return 0; /* Skip this one. */ } if (opt.with_colons) @@ -2268,24 +2372,34 @@ list_keyblock (ctrl_t ctrl, else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); - if (secret) - es_fflush (es_stdout); + if (es_ferror (es_stdout)) + err = gpg_error_from_syserror (); + + if (secret && es_fflush (es_stdout) && !err) + err = gpg_error_from_syserror (); + + if (err) + log_error (_("error writing to stdout: %s\n"), gpg_strerror (err)); + + return err; } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ -void +gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; + gpg_error_t err; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; - list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); + err = list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); + return err; } diff --git a/g10/keyring.c b/g10/keyring.c index baddf5d54..0fe8bcd9c 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1148,7 +1148,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, if (need_keyid) keyid_from_pk (pk, aki); if (need_grip) - keygrip_from_pk (pk, grip); + keygrip_from_pk (pk, grip, 0); if (use_key_present_hash && !key_present_hash_ready diff --git a/g10/main.h b/g10/main.h index 2482fbde2..2443aa7fe 100644 --- a/g10/main.h +++ b/g10/main.h @@ -108,7 +108,7 @@ char *make_radix64_string( const byte *data, size_t len ); void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); -int is_secured_file (int fd); +int is_secured_file (gnupg_fd_t fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( const byte *p, unsigned n ); @@ -243,9 +243,9 @@ aead_algo_t use_aead (pk_list_t pk_list, int algo); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); -int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, +int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, - int outputfd); + gnupg_fd_t outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, @@ -341,7 +341,7 @@ gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); -int open_outfile (int out_fd, const char *iname, int mode, +int open_outfile (gnupg_fd_t out_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); @@ -470,8 +470,8 @@ void secret_key_list (ctrl_t ctrl, strlist_t list ); gpg_error_t parse_and_set_list_filter (const char *string); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); -void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, - int has_secret, int fpr, int no_validity); +gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, + int has_secret, int fpr, int no_validity); int cmp_signodes (const void *av, const void *bv); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); @@ -493,20 +493,22 @@ void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); -int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); +int gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd, + estream_t out_fp); void check_assert_signer_list (const char *mainpkhex, const char *pkhex); void check_assert_pubkey_algo (const char *algostr, const char *pkhex); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); -gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); +gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, + gnupg_fd_t output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); -int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, - int textmode ); +int hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, + gnupg_fd_t data_fd, int textmode); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index e722618ca..91ababbb6 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -81,7 +81,7 @@ struct mainproc_context struct { /* A file descriptor of the signed data. Only used if not -1. */ - int data_fd; + gnupg_fd_t data_fd; /* A list of filenames with the data files or NULL. This is only used if DATA_FD is -1. */ strlist_t data_names; @@ -143,6 +143,8 @@ release_list( CTX c ) mpi_release (c->pkenc_list->data[0]); mpi_release (c->pkenc_list->data[1]); + mpi_release (c->pkenc_list->data[2]); + mpi_release (c->pkenc_list->data[3]); xfree (c->pkenc_list); c->pkenc_list = tmp; } @@ -527,11 +529,14 @@ proc_pubkey_enc (CTX c, PACKET *pkt) x->keyid[1] = enc->keyid[1]; x->pubkey_algo = enc->pubkey_algo; x->result = -1; - x->data[0] = x->data[1] = NULL; + x->seskey_algo = enc->seskey_algo; + x->data[0] = x->data[1] = x->data[2] = x->data[3] = NULL; if (enc->data[0]) { x->data[0] = mpi_copy (enc->data[0]); x->data[1] = mpi_copy (enc->data[1]); + x->data[2] = mpi_copy (enc->data[2]); + x->data[3] = mpi_copy (enc->data[3]); } x->next = c->pkenc_list; c->pkenc_list = x; @@ -573,6 +578,10 @@ print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list) openpgp_pk_algo_name (list->pubkey_algo), keystr(list->keyid)); + if (opt.flags.require_pqc_encryption + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + log_info (_("WARNING: key is not quantum-resistant\n")); + free_public_key (pk); } } @@ -1097,7 +1106,7 @@ static int proc_compressed_cb (iobuf_t a, void *info) { if ( ((CTX)info)->signed_data.used - && ((CTX)info)->signed_data.data_fd != -1) + && ((CTX)info)->signed_data.data_fd != GNUPG_INVALID_FD) return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_fd); else @@ -1519,7 +1528,7 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, c->anchor = anchor; c->sigs_only = 1; - c->signed_data.data_fd = -1; + c->signed_data.data_fd = GNUPG_INVALID_FD; c->signed_data.data_names = signedfiles; c->signed_data.used = !!signedfiles; @@ -1549,8 +1558,8 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, int -proc_signature_packets_by_fd (ctrl_t ctrl, - void *anchor, iobuf_t a, int signed_data_fd ) +proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a, + gnupg_fd_t signed_data_fd) { int rc; CTX c; @@ -1565,7 +1574,7 @@ proc_signature_packets_by_fd (ctrl_t ctrl, c->signed_data.data_fd = signed_data_fd; c->signed_data.data_names = NULL; - c->signed_data.used = (signed_data_fd != -1); + c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD); rc = do_proc_packets (c, a); @@ -2549,8 +2558,6 @@ check_sig_and_print (CTX c, kbnode_t node) release_kbnode( keyblock ); if (rc) g10_errors_seen = 1; - if (opt.batch && rc) - g10_exit (1); } else /* Error checking the signature. (neither Good nor Bad). */ { @@ -2636,7 +2643,8 @@ proc_tree (CTX c, kbnode_t node) /* Ask for file and hash it. */ if (c->sigs_only) { - if (c->signed_data.used && c->signed_data.data_fd != -1) + if (c->signed_data.used + && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, NULL, c->signed_data.data_fd, use_textmode); @@ -2667,7 +2675,8 @@ proc_tree (CTX c, kbnode_t node) } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) - check_sig_and_print (c, n1); + if (check_sig_and_print (c, n1) && opt.batch) + break; } else if (node->pkt->pkttype == PKT_GPG_CONTROL @@ -2686,8 +2695,8 @@ proc_tree (CTX c, kbnode_t node) } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) - check_sig_and_print (c, n1); - + if (check_sig_and_print (c, n1) && opt.batch) + break; } else if (node->pkt->pkttype == PKT_SIGNATURE) { @@ -2779,7 +2788,8 @@ proc_tree (CTX c, kbnode_t node) if (c->sigs_only) { - if (c->signed_data.used && c->signed_data.data_fd != -1) + if (c->signed_data.used + && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2, c->signed_data.data_fd, (sig->sig_class == 0x01)); @@ -2814,7 +2824,8 @@ proc_tree (CTX c, kbnode_t node) if (multiple_ok) { for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) - check_sig_and_print (c, n1); + if (check_sig_and_print (c, n1) && opt.batch) + break; } else check_sig_and_print (c, node); diff --git a/g10/mdfilter.c b/g10/mdfilter.c index f3318f15c..a655d6d72 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <npth.h> #include "gpg.h" #include "../common/status.h" @@ -71,3 +72,297 @@ free_md_filter_context( md_filter_context_t *mfx ) mfx->md2 = NULL; mfx->maxbuf_size = 0; } + + +/**************** + * Threaded implementation for hashing. + */ + +struct md_thd_filter_context { + gcry_md_hd_t md; + npth_t thd; + /**/ + npth_mutex_t mutex; + npth_cond_t cond; + size_t bufsize; + unsigned int produce : 1; + unsigned int consume : 1; + ssize_t written0; + ssize_t written1; + unsigned char buf[1]; +}; + + +static void +lock_md (struct md_thd_filter_context *mfx) +{ + int rc = npth_mutex_lock (&mfx->mutex); + if (rc) + log_fatal ("%s: failed to acquire mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + + +static void +unlock_md (struct md_thd_filter_context * mfx) +{ + int rc = npth_mutex_unlock (&mfx->mutex); + if (rc) + log_fatal ("%s: failed to release mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + +static int +get_buffer_to_hash (struct md_thd_filter_context *mfx, + unsigned char **r_buf, size_t *r_len) +{ + int rc = 0; + + lock_md (mfx); + + if ((mfx->consume == 0 && mfx->written0 < 0) + || (mfx->consume != 0 && mfx->written1 < 0)) + { + rc = npth_cond_wait (&mfx->cond, &mfx->mutex); + if (rc) + { + unlock_md (mfx); + return -1; + } + } + + if (mfx->consume == 0) + { + *r_buf = mfx->buf; + *r_len = mfx->written0; + } + else + { + *r_buf = mfx->buf + mfx->bufsize; + *r_len = mfx->written1; + } + + unlock_md (mfx); + + return 0; +} + +static int +put_buffer_to_recv (struct md_thd_filter_context *mfx) +{ + int rc = 0; + + lock_md (mfx); + if (mfx->consume == 0) + { + mfx->written0 = -1; + mfx->consume = 1; + } + else + { + mfx->written1 = -1; + mfx->consume = 0; + } + + rc = npth_cond_signal (&mfx->cond); + if (rc) + { + unlock_md (mfx); + return -1; + } + + unlock_md (mfx); + return 0; +} + +static int +get_buffer_to_fill (struct md_thd_filter_context *mfx, + unsigned char **r_buf, size_t len) +{ + lock_md (mfx); + + if (len > mfx->bufsize) + { + unlock_md (mfx); + return GPG_ERR_BUFFER_TOO_SHORT; + } + + if ((mfx->produce == 0 && mfx->written0 >= 0) + || (mfx->produce != 0 && mfx->written1 >= 0)) + { + int rc = npth_cond_wait (&mfx->cond, &mfx->mutex); + if (rc) + { + unlock_md (mfx); + return gpg_error_from_errno (rc); + } + } + + if (mfx->produce == 0) + *r_buf = mfx->buf; + else + *r_buf = mfx->buf + mfx->bufsize; + unlock_md (mfx); + return 0; +} + +static int +put_buffer_to_send (struct md_thd_filter_context *mfx, size_t len) +{ + int rc; + + lock_md (mfx); + if (mfx->produce == 0) + { + mfx->written0 = len; + mfx->produce = 1; + } + else + { + mfx->written1 = len; + mfx->produce = 0; + } + + rc = npth_cond_signal (&mfx->cond); + if (rc) + { + unlock_md (mfx); + return gpg_error_from_errno (rc); + } + + unlock_md (mfx); + + /* Yield to the md_thread to let it compute the hash in parallel */ + npth_usleep (0); + return 0; +} + + +static void * +md_thread (void *arg) +{ + struct md_thd_filter_context *mfx = arg; + + while (1) + { + unsigned char *buf; + size_t len; + + if (get_buffer_to_hash (mfx, &buf, &len) < 0) + /* Error */ + return NULL; + + if (len == 0) + break; + + npth_unprotect (); + gcry_md_write (mfx->md, buf, len); + npth_protect (); + + if (put_buffer_to_recv (mfx) < 0) + /* Error */ + return NULL; + } + + return NULL; +} + +int +md_thd_filter (void *opaque, int control, + IOBUF a, byte *buf, size_t *ret_len) +{ + size_t size = *ret_len; + struct md_thd_filter_context **r_mfx = opaque; + struct md_thd_filter_context *mfx = *r_mfx; + int rc=0; + + if (control == IOBUFCTRL_INIT) + { + npth_attr_t tattr; + size_t n; + + n = 2 * iobuf_set_buffer_size (0) * 1024; + mfx = xtrymalloc (n + offsetof (struct md_thd_filter_context, buf)); + if (!mfx) + return gpg_error_from_syserror (); + *r_mfx = mfx; + mfx->bufsize = n / 2; + mfx->consume = mfx->produce = 0; + mfx->written0 = -1; + mfx->written1 = -1; + + rc = npth_mutex_init (&mfx->mutex, NULL); + if (rc) + { + return gpg_error_from_errno (rc); + } + rc = npth_cond_init (&mfx->cond, NULL); + if (rc) + { + npth_mutex_destroy (&mfx->mutex); + return gpg_error_from_errno (rc); + } + rc = npth_attr_init (&tattr); + if (rc) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + return gpg_error_from_errno (rc); + } + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + rc = npth_create (&mfx->thd, &tattr, md_thread, mfx); + if (rc) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + npth_attr_destroy (&tattr); + return gpg_error_from_errno (rc); + } + npth_attr_destroy (&tattr); + } + else if (control == IOBUFCTRL_UNDERFLOW) + { + int i; + unsigned char *md_buf = NULL; + + i = iobuf_read (a, buf, size); + if (i == -1) + i = 0; + + rc = get_buffer_to_fill (mfx, &md_buf, i); + if (rc) + return rc; + + if (i) + memcpy (md_buf, buf, i); + + rc = put_buffer_to_send (mfx, i); + if (rc) + return rc; + + if (i == 0) + { + npth_join (mfx->thd, NULL); + rc = -1; /* eof */ + } + + *ret_len = i; + } + else if (control == IOBUFCTRL_FREE) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + xfree (mfx); + *r_mfx = NULL; + } + else if (control == IOBUFCTRL_DESC) + mem2str (buf, "md_thd_filter", *ret_len); + + return rc; +} + +void +md_thd_filter_set_md (struct md_thd_filter_context *mfx, gcry_md_hd_t md) +{ + mfx->md = md; +} diff --git a/g10/misc.c b/g10/misc.c index 2f4b452dd..c52091830 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -160,7 +160,7 @@ unregister_secured_file (const char *fname) /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int -is_secured_file (int fd) +is_secured_file (gnupg_fd_t fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; @@ -750,6 +750,8 @@ openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) ga = GCRY_PK_ELG; break; + case PUBKEY_ALGO_KYBER: ga = GCRY_PK_KEM; break; + default: break; } @@ -799,6 +801,18 @@ openpgp_pk_algo_usage ( int algo ) case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; + break; + + case PUBKEY_ALGO_KYBER: + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; + break; + + case PUBKEY_ALGO_DIL3_25519: + case PUBKEY_ALGO_DIL5_448: + case PUBKEY_ALGO_SPHINX_SHA2: + use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; + break; + default: break; } @@ -822,6 +836,7 @@ openpgp_pk_algo_name (pubkey_algo_t algo) case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; + case PUBKEY_ALGO_KYBER: return "Kyber"; default: return "?"; } } @@ -1711,6 +1726,7 @@ pubkey_get_npkey (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; + case PUBKEY_ALGO_KYBER: return 3; default: return 0; } } @@ -1731,6 +1747,7 @@ pubkey_get_nskey (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; + case PUBKEY_ALGO_KYBER: return 5; default: return 0; } } @@ -1770,6 +1787,7 @@ pubkey_get_nenc (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; + case PUBKEY_ALGO_KYBER: return 3; default: return 0; } } diff --git a/g10/openfile.c b/g10/openfile.c index 5ca168a13..01f323399 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -179,13 +179,13 @@ ask_outfile_name( const char *name, size_t namelen ) * be closed if the returned IOBUF is closed. This is used for gpg's * --server mode. */ int -open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, - iobuf_t *a) +open_outfile (gnupg_fd_t out_fd, const char *iname, int mode, + int restrictedperm, iobuf_t *a) { int rc = 0; *a = NULL; - if (out_fd != -1) + if (out_fd != GNUPG_INVALID_FD) { char xname[64]; @@ -193,12 +193,12 @@ open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, if (!*a) { rc = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", out_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { - snprintf (xname, sizeof xname, "[fd %d]", out_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd)); log_info (_("writing to '%s'\n"), xname); } } diff --git a/g10/options.h b/g10/options.h index 50fa4ad86..ae429fcc1 100644 --- a/g10/options.h +++ b/g10/options.h @@ -283,6 +283,7 @@ struct /* Fail if an operation can't be done in the requested compliance * mode. */ unsigned int require_compliance:1; + unsigned int require_pqc_encryption:1; } flags; /* Linked list of ways to find a key if the key isn't on the local @@ -378,7 +379,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; /* Compatibility flags */ -/* #define COMPAT_FOO 1 */ +#define COMPAT_PARALLELIZED 1 /* Use threaded hashing for signatures. */ +#define COMPAT_T7014_OLD 2 /* Use initial T7014 test data. */ /* Compliance test macors. */ @@ -442,6 +444,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SHOW_PREF (1<<14) #define LIST_SHOW_PREF_VERBOSE (1<<15) #define LIST_SHOW_UNUSABLE_SIGS (1<<16) +#define LIST_SHOW_X509_NOTATIONS (1<<17) +#define LIST_STORE_X509_NOTATIONS (1<<18) #define LIST_SHOW_OWNERTRUST (1<<19) #define VERIFY_SHOW_PHOTOS (1<<0) diff --git a/g10/packet.h b/g10/packet.h index 39dab96c9..459e38dda 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -137,6 +137,8 @@ typedef struct { byte version; /* The algorithm used for the public key encryption scheme. */ byte pubkey_algo; + /* The session key algorithm used by some pubkey algos. */ + byte seskey_algo; /* Whether to hide the key id. This value is not directly serialized. */ byte throw_keyid; @@ -151,6 +153,7 @@ struct pubkey_enc_list struct pubkey_enc_list *next; u32 keyid[2]; int pubkey_algo; + int seskey_algo; int result; gcry_mpi_t data[PUBKEY_MAX_NENC]; }; @@ -342,11 +345,11 @@ struct revoke_info /* Information pertaining to secret keys. */ struct seckey_info { - int is_protected:1; /* The secret info is protected and must */ + unsigned int is_protected:1; /* The secret info is protected and must */ /* be decrypted before use, the protected */ /* MPIs are simply (void*) pointers to memory */ /* and should never be passed to a mpi_xxx() */ - int sha1chk:1; /* SHA1 is used instead of a 16 bit checksum */ + unsigned int sha1chk:1; /* SHA1 is used instead of a 16 bit checksum */ u16 csum; /* Checksum for old protection modes. */ byte algo; /* Cipher used to protect the secret information. */ STRING2KEY s2k; /* S2K parameter. */ @@ -602,8 +605,8 @@ struct notation /* Sometimes we want to %-expand the value. In these cases, we save that transformed value here. */ char *altvalue; - /* If the notation is not human readable, then the value is stored - here. */ + /* If the notation is not human readable or the function does not + want to distinguish that, then the value is stored here. */ unsigned char *bdat; /* The amount of data stored in BDAT. @@ -634,8 +637,8 @@ void reset_literals_seen(void); int proc_packets (ctrl_t ctrl, void *ctx, iobuf_t a ); int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a, strlist_t signedfiles, const char *sigfile ); -int proc_signature_packets_by_fd (ctrl_t ctrl, - void *anchor, IOBUF a, int signed_data_fd ); +int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a, + gnupg_fd_t signed_data_fd); int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a); int list_packets( iobuf_t a ); @@ -863,7 +866,9 @@ gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf); int build_packet (iobuf_t out, PACKET *pkt); gpg_error_t build_packet_and_meta (iobuf_t out, PACKET *pkt); gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a, unsigned int *t_nwritten); -gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write_opaque_32 (iobuf_t out, gcry_mpi_t a, + unsigned int *r_nwritten); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); @@ -877,7 +882,8 @@ struct notation *string_to_notation(const char *string,int is_utf8); struct notation *blob_to_notation(const char *name, const char *data, size_t len); struct notation *sig_to_notation(PKT_signature *sig); -void free_notation(struct notation *notation); +struct notation *search_sig_notations (PKT_signature *sig, const char *name); +void free_notation (struct notation *notation); /*-- free-packet.c --*/ void free_symkey_enc( PKT_symkey_enc *enc ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index aa6bac9da..8bd283b4b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -188,6 +188,109 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) } +/* If NLENGTH is zero read an octet string of length NBYTES from INP + * and return it at R_DATA. + * + * If NLENGTH is either 1, 2, or 4 and NLENGTH is zero read an + * NLENGTH-octet count and use this count number octets from INP and + * return it at R_DATA. + * + * On error return an error code and store NULL at R_DATA. PKTLEN + * shall give the current length of the packet and is updated with + * each read. If SECURE is true, the integer is stored in secure + * memory (allocated using gcry_xmalloc_secure). + */ +static gpg_error_t +read_octet_string (iobuf_t inp, unsigned long *pktlen, + unsigned int nlength, unsigned int nbytes, + int secure, gcry_mpi_t *r_data) +{ + gpg_error_t err; + int c, i; + byte *buf = NULL; + byte *p; + + *r_data = NULL; + + if ((nbytes && nlength) + || (!nbytes && !(nlength == 1 || nlength == 2 || nlength == 4))) + { + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + if (nlength) + { + for (i = 0; i < nlength; i++) + { + if (!*pktlen) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + c = iobuf_readbyte (inp); + if (c < 0) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + --*pktlen; + nbytes <<= 8; + nbytes |= c; + } + + if (!nbytes) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + } + + if (nbytes*8 > (nbytes==4? MAX_EXTERN_KEYPARM_BITS:MAX_EXTERN_MPI_BITS) + || (nbytes*8 < nbytes)) + { + log_error ("octet string too large (%u octets)\n", nbytes); + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + if (nbytes > *pktlen) + { + log_error ("octet string larger than packet (%u octets)\n", nbytes); + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + buf = secure ? gcry_malloc_secure (nbytes) : gcry_malloc (nbytes); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + p = buf; + for (i = 0; i < nbytes; i++) + { + c = iobuf_get (inp); + if (c == -1) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + p[i] = c; + --*pktlen; + } + + *r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8); + gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2); + return 0; + + leave: + gcry_free (buf); + return err; +} + + /* Read an external representation of an SOS and return the opaque MPI with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned value stored in network byte order giving information for the @@ -1102,32 +1205,29 @@ read_rest (IOBUF inp, size_t pktlen) /* Read a special size+body from INP. On success store an opaque MPI - with it at R_DATA. On error return an error code and store NULL at - R_DATA. Even in the error case store the number of read bytes at - R_NREAD. The caller shall pass the remaining size of the packet in - PKTLEN. */ + * with it at R_DATA. The caller shall store the remaining size of + * the packet at PKTLEN. On error return an error code and store NULL + * at R_DATA. Even in the error case store the number of read bytes + * at PKTLEN is updated. */ static gpg_error_t -read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, - gcry_mpi_t *r_data) +read_sized_octet_string (iobuf_t inp, unsigned long *pktlen, gcry_mpi_t *r_data) { char buffer[256]; char *tmpbuf; int i, c, nbytes; - *r_nread = 0; *r_data = NULL; - if (!pktlen) + if (!*pktlen) return gpg_error (GPG_ERR_INV_PACKET); c = iobuf_readbyte (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); - pktlen--; - ++*r_nread; + --*pktlen; nbytes = c; if (nbytes < 2 || nbytes > 254) return gpg_error (GPG_ERR_INV_PACKET); - if (nbytes > pktlen) + if (nbytes > *pktlen) return gpg_error (GPG_ERR_INV_PACKET); buffer[0] = nbytes; @@ -1137,7 +1237,7 @@ read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, c = iobuf_get (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); - ++*r_nread; + --*pktlen; buffer[1+i] = c; } @@ -1344,12 +1444,14 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } +/* Parse a public key encrypted packet (Tag 1). */ static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { int rc = 0; int i, ndata; + unsigned int n; PKT_pubkey_enc *k; k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc); @@ -1392,46 +1494,78 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, unknown_pubkey_warning (k->pubkey_algo); k->data[0] = NULL; /* No need to store the encrypted data. */ } + else if (k->pubkey_algo == PUBKEY_ALGO_ECDH) + { + log_assert (ndata == 2); + /* Get the ephemeral public key. */ + n = pktlen; + k->data[0] = sos_read (inp, &n, 0); + pktlen -= n; + if (!k->data[0]) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + /* Get the wrapped symmetric key. */ + rc = read_sized_octet_string (inp, &pktlen, k->data + 1); + if (rc) + goto leave; + } + else if (k->pubkey_algo == PUBKEY_ALGO_KYBER) + { + log_assert (ndata == 3); + /* Get the ephemeral public key. */ + n = pktlen; + k->data[0] = sos_read (inp, &n, 0); + pktlen -= n; + if (!k->data[0]) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + /* Get the Kyber ciphertext. */ + rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1); + if (rc) + goto leave; + /* Get the algorithm id for the session key. */ + if (!pktlen) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + k->seskey_algo = iobuf_get_noeof (inp); + pktlen--; + /* Get the encrypted symmetric key. */ + rc = read_octet_string (inp, &pktlen, 1, 0, 0, k->data + 2); + if (rc) + goto leave; + } else { for (i = 0; i < ndata; i++) { - if (k->pubkey_algo == PUBKEY_ALGO_ECDH) - { - if (i == 1) - { - size_t n; - rc = read_size_body (inp, pktlen, &n, k->data+i); - pktlen -= n; - } - else - { - int n = pktlen; - k->data[i] = sos_read (inp, &n, 0); - pktlen -= n; - if (!k->data[i]) - rc = gpg_error (GPG_ERR_INV_PACKET); - } - } - else - { - int n = pktlen; - k->data[i] = mpi_read (inp, &n, 0); - pktlen -= n; - if (!k->data[i]) - rc = gpg_error (GPG_ERR_INV_PACKET); - } - if (rc) - goto leave; - if (list_mode) - { - es_fprintf (listfp, "\tdata: "); - mpi_print (listfp, k->data[i], mpi_print_mode); - es_putc ('\n', listfp); - } + n = pktlen; + k->data[i] = mpi_read (inp, &n, 0); + pktlen -= n; + if (!k->data[i]) + rc = gpg_error (GPG_ERR_INV_PACKET); + } + if (rc) + goto leave; + } + if (list_mode) + { + if (k->seskey_algo) + es_fprintf (listfp, "\tsession key algo: %d\n", k->seskey_algo); + for (i = 0; i < ndata; i++) + { + es_fprintf (listfp, "\tdata: "); + mpi_print (listfp, k->data[i], mpi_print_mode); + es_putc ('\n', listfp); } } + leave: iobuf_skip_rest (inp, pktlen, 0); return rc; @@ -2598,19 +2732,25 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, { if ( (algorithm == PUBKEY_ALGO_ECDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0)) - || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) + || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)) + || (algorithm == PUBKEY_ALGO_KYBER && (i == 0))) { /* Read the OID (i==0) or the KDF params (i==2). */ - size_t n; - err = read_size_body (inp, pktlen, &n, pk->pkey+i); - pktlen -= n; + err = read_sized_octet_string (inp, &pktlen, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_KYBER && i == 2) + { + /* Read the four-octet count prefixed Kyber public key. */ + err = read_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i); } else { + /* Read MPI or SOS. */ unsigned int n = pktlen; if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) pk->pkey[i] = sos_read (inp, &n, 0); else pk->pkey[i] = mpi_read (inp, &n, 0); @@ -2626,7 +2766,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, mpi_print (listfp, pk->pkey[i], mpi_print_mode); if ((algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) && i==0) + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) && i==0) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); @@ -2963,21 +3104,32 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, /* Not encrypted. */ for (i = npkey; i < nskey; i++) { - unsigned int n; if (pktlen < 2) /* At least two bytes for the length. */ { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - n = pktlen; - if (algorithm == PUBKEY_ALGO_ECDSA - || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) - pk->pkey[i] = sos_read (inp, &n, 0); + if (algorithm == PUBKEY_ALGO_KYBER && i == npkey+1) + { + err = read_octet_string (inp, &pktlen, 4, 0, 1, pk->pkey+i); + if (err) + goto leave; + } else - pk->pkey[i] = mpi_read (inp, &n, 0); - pktlen -= n; + { + unsigned int n = pktlen; + + if (algorithm == PUBKEY_ALGO_ECDSA + || algorithm == PUBKEY_ALGO_EDDSA + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) + pk->pkey[i] = sos_read (inp, &n, 0); + else + pk->pkey[i] = mpi_read (inp, &n, 0); + pktlen -= n; + } + if (list_mode) { es_fprintf (listfp, "\tskey[%d]: ", i); diff --git a/g10/photoid.c b/g10/photoid.c index fc8866121..8cc7e3a20 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -27,9 +27,6 @@ # include <winsock2.h> # endif # include <windows.h> -# ifndef VER_PLATFORM_WIN32_WINDOWS -# define VER_PLATFORM_WIN32_WINDOWS 1 -# endif #endif #include "gpg.h" @@ -95,8 +92,15 @@ w32_system (const char *command) return -1; } if (DBG_EXTPROG) - log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", - see.hProcess, (int)see.hInstApp); + { + /* hInstApp has HINSTANCE type. The documentations says + that it's not a true HINSTANCE and it can be cast only to + an int. */ + int hinstance = (intptr_t)see.hInstApp; + + log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", + see.hProcess, hinstance); + } if (!see.hProcess) { @@ -381,16 +385,7 @@ static const char * get_default_photo_command(void) { #if defined(_WIN32) - OSVERSIONINFO osvi; - - memset(&osvi,0,sizeof(osvi)); - osvi.dwOSVersionInfoSize=sizeof(osvi); - GetVersionEx(&osvi); - - if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) - return "start /w %i"; - else - return "!ShellExecute 400 %i"; + return "!ShellExecute 400 %i"; #elif defined(__APPLE__) /* OS X. This really needs more than just __APPLE__. */ return "open %I"; @@ -600,34 +595,35 @@ run_with_pipe (struct spawn_info *info, const void *image, u32 len) " external programs\n")); return; #else /* !EXEC_TEMPFILE_ONLY */ - int to[2]; - pid_t pid; gpg_error_t err; const char *argv[4]; - - err = gnupg_create_pipe (to); - if (err) - return; + gnupg_process_t proc; fill_command_argv (argv, info->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, to[0], -1, -1, &pid); - - close (to[0]); - + err = gnupg_process_spawn (argv[0], argv+1, GNUPG_PROCESS_STDIN_PIPE, + NULL, NULL, &proc); if (err) - { - log_error (_("unable to execute shell '%s': %s\n"), - argv[0], gpg_strerror (err)); - close (to[1]); - } + log_error (_("unable to execute shell '%s': %s\n"), + argv[0], gpg_strerror (err)); else { - write (to[1], image, len); - close (to[1]); + int fd_in; + + err = gnupg_process_get_fds (proc, 0, &fd_in, NULL, NULL); + if (err) + log_error ("unable to get pipe connection '%s': %s\n", + argv[2], gpg_strerror (err)); + else + { + write (fd_in, image, len); + close (fd_in); + } - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) log_error (_("unnatural exit of external program\n")); + + gnupg_process_release (proc); } #endif /* !EXEC_TEMPFILE_ONLY */ } @@ -695,14 +691,11 @@ show_photo (const char *command, const char *name, const void *image, u32 len) log_error (_("system error while calling external program: %s\n"), strerror (errno)); #else - pid_t pid; gpg_error_t err; const char *argv[4]; fill_command_argv (argv, spawn->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, -1, -1, -1, &pid); - if (!err) - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_spawn (argv[0], argv+1, 0, NULL, NULL, NULL); if (err) log_error (_("unnatural exit of external program\n")); #endif diff --git a/g10/pkglue.c b/g10/pkglue.c index f18313913..f4efa8fc5 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -1,6 +1,7 @@ /* pkglue.c - public key operations glue code * Copyright (C) 2000, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch + * Copyright (C) 2024 g10 Code GmbH. * * This file is part of GnuPG. * @@ -16,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -30,6 +32,12 @@ #include "main.h" #include "options.h" + +/* Maximum buffer sizes required for ECC KEM. */ +#define ECC_POINT_LEN_MAX (1+2*64) +#define ECC_HASH_LEN_MAX 64 + + /* FIXME: Better change the function name because mpi_ is used by gcrypt macros. */ gcry_mpi_t @@ -415,140 +423,505 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, } +#if GCRY_KEM_MLKEM1024_ENCAPS_LEN < GCRY_KEM_MLKEM768_ENCAPS_LEN \ + || GCRY_KEM_MLKEM1024_SHARED_LEN < GCRY_KEM_MLKEM768_SHARED_LEN +# error Bad Kyber constants in Libgcrypt +#endif - -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - * PK is only required to compute the fingerprint for ECDH. - */ -int -pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, - PKT_public_key *pk, gcry_mpi_t *pkey) +/* Core of the encryption for KEM algorithms. See pk_decrypt for a + * description of the arguments. */ +static gpg_error_t +do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr) { - gcry_sexp_t s_ciph = NULL; + gpg_error_t err; + int i; + unsigned int nbits, n; gcry_sexp_t s_data = NULL; - gcry_sexp_t s_pkey = NULL; - int rc; - - /* Make a sexp from pkey. */ - if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) - { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - pkey[0], pkey[1], pkey[2]); - /* Put DATA into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", data); - } - else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) + gcry_cipher_hd_t hd = NULL; + char *ecc_oid = NULL; + enum gcry_kem_algos kyber_algo, ecc_algo; + + const unsigned char *ecc_pubkey; + size_t ecc_pubkey_len; + const unsigned char *kyber_pubkey; + size_t kyber_pubkey_len; + const unsigned char *seskey; + size_t seskey_len; + unsigned char *enc_seskey = NULL; + size_t enc_seskey_len; + int ecc_hash_algo; + + unsigned char ecc_ct[ECC_POINT_LEN_MAX]; + unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; + unsigned char ecc_ss[ECC_HASH_LEN_MAX]; + size_t ecc_ct_len, ecc_ecdh_len, ecc_ss_len; + + unsigned char kyber_ct[GCRY_KEM_MLKEM1024_ENCAPS_LEN]; + unsigned char kyber_ss[GCRY_KEM_MLKEM1024_SHARED_LEN]; + size_t kyber_ct_len, kyber_ss_len; + + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + unsigned char kek[32]; /* AES-256 is mandatory. */ + size_t kek_len = 32; + + /* For later error checking we make sure the array is cleared. */ + resarr[0] = resarr[1] = resarr[2] = NULL; + + /* As of now we use KEM only for the combined Kyber and thus a + * second public key is expected. Right now we take the keys + * directly from the PK->data elements. */ + + ecc_oid = openpgp_oid_to_str (pk->pkey[0]); + if (!ecc_oid) { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(rsa(n%m)(e%m)))", - pkey[0], pkey[1]); - /* Put DATA into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", data); + err = gpg_error_from_syserror (); + log_error ("%s: error getting OID for ECC key\n", __func__); + goto leave; } - else if (algo == PUBKEY_ALGO_ECDH) + ecc_algo = openpgp_oid_to_kem_algo (ecc_oid); + if (ecc_algo == GCRY_KEM_RAW_X25519) { - gcry_mpi_t k; - - rc = pk_ecdh_generate_ephemeral_key (pkey, &k); - if (!rc) + if (!strcmp (ecc_oid, "1.3.6.1.4.1.3029.1.5.1")) + log_info ("Warning: " + "legacy OID for cv25519 accepted during develpment\n"); + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) { - char *curve; - - curve = openpgp_oid_to_str (pkey[0]); - if (!curve) - rc = gpg_error_from_syserror (); - else - { - int with_djb_tweak_flag = openpgp_oid_is_cv25519 (pkey[0]); - - /* Now use the ephemeral secret to compute the shared point. */ - rc = gcry_sexp_build (&s_pkey, NULL, - with_djb_tweak_flag ? - "(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))" - : "(public-key(ecdh(curve%s)(q%m)))", - curve, pkey[1]); - xfree (curve); - /* Put K into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", k); - } - gcry_mpi_release (k); + ecc_pubkey++; /* Remove the 0x40 prefix. */ + ecc_pubkey_len--; } - } - else - rc = gpg_error (GPG_ERR_PUBKEY_ALGO); - - /* Pass it to libgcrypt. */ - if (!rc) - rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); - - gcry_sexp_release (s_data); - gcry_sexp_release (s_pkey); - - if (rc) - ; - else if (algo == PUBKEY_ALGO_ECDH) - { - gcry_mpi_t public, result; - byte fp[MAX_FINGERPRINT_LEN]; - byte *shared; - size_t nshared; - - /* Get the shared point and the ephemeral public key. */ - shared = get_data_from_sexp (s_ciph, "s", &nshared); - if (!shared) + if (ecc_pubkey_len != 32) { - rc = gpg_error_from_syserror (); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); goto leave; } - rc = sexp_extract_param_sos (s_ciph, "e", &public); - gcry_sexp_release (s_ciph); - s_ciph = NULL; - if (DBG_CRYPTO) + ecc_ct_len = ecc_ecdh_len = 32; + ecc_ss_len = 32; + ecc_hash_algo = GCRY_MD_SHA3_256; + } + else if (ecc_algo == GCRY_KEM_RAW_X448) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 56) { - log_debug ("ECDH ephemeral key:"); - gcry_mpi_dump (public); - log_printf ("\n"); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - - result = NULL; - fingerprint_from_pk (pk, fp, NULL); - - if (!rc) + ecc_ct_len = ecc_ecdh_len = 56; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; + } + else if (ecc_algo == GCRY_KEM_RAW_BP256) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 65) { - unsigned int nbits; - byte *p = gcry_mpi_get_opaque (data, &nbits); - rc = pk_ecdh_encrypt_with_shared_point (shared, nshared, fp, p, - (nbits+7)/8, pkey, &result); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - xfree (shared); - if (!rc) + ecc_ct_len = ecc_ecdh_len = 65; + ecc_ss_len = 32; + ecc_hash_algo = GCRY_MD_SHA3_256; + } + else if (ecc_algo == GCRY_KEM_RAW_BP384) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 97) { - resarr[0] = public; - resarr[1] = result; + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - else + ecc_ct_len = ecc_ecdh_len = 97; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; + } + else if (ecc_algo == GCRY_KEM_RAW_BP512) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 129) { - gcry_mpi_release (public); - gcry_mpi_release (result); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } + ecc_ct_len = ecc_ecdh_len = 129; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; } - else /* Elgamal or RSA case. */ - { /* Fixme: Add better error handling or make gnupg use - S-expressions directly. */ - resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); - if (!is_RSA (algo)) - resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); + else + { + if (opt.verbose) + log_info ("%s: ECC curve %s not supported\n", __func__, ecc_oid); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + + if (DBG_CRYPTO) + { + log_debug ("ECC curve: %s\n", ecc_oid); + log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); } + err = gcry_kem_encap (ecc_algo, + ecc_pubkey, ecc_pubkey_len, + ecc_ct, ecc_ct_len, + ecc_ecdh, ecc_ecdh_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", + __func__, ecc_oid); + goto leave; + } + if (DBG_CRYPTO) + { + log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); + log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); + } + err = gnupg_ecc_kem_kdf (ecc_ss, ecc_ss_len, + ecc_hash_algo, + ecc_ecdh, ecc_ecdh_len, + ecc_ct, ecc_ct_len, + ecc_pubkey, ecc_pubkey_len); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); + + kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[2], &nbits); + kyber_pubkey_len = (nbits+7)/8; + if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM768; + kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; + } + else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM1024; + kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; + } + else + { + if (opt.verbose) + log_info ("%s: Kyber public key length invalid (%zu)\n", + __func__, kyber_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); + + err = gcry_kem_encap (kyber_algo, + kyber_pubkey, kyber_pubkey_len, + kyber_ct, kyber_ct_len, + kyber_ss, kyber_ss_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); + goto leave; + } + + if (DBG_CRYPTO) + { + log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); + log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); + } + + + fixedinfo[0] = seskey_algo; + v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); + fixedlen = 33; + + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_ss_len, ecc_ct, ecc_ct_len, + kyber_ss, kyber_ss_len, kyber_ct, kyber_ct_len, + fixedinfo, fixedlen); + if (err) + { + if (opt.verbose) + log_info ("%s: KEM combiner failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kek, kek_len, "KEK:"); + + err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (!err) + err = gcry_cipher_setkey (hd, kek, kek_len); + if (err) + { + if (opt.verbose) + log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, + gpg_strerror (err)); + goto leave; + } + + err = gcry_sexp_build (&s_data, NULL, "%m", data); + if (err) + goto leave; + + n = gcry_cipher_get_algo_keylen (seskey_algo); + seskey = gcry_mpi_get_opaque (data, &nbits); + seskey_len = (nbits+7)/8; + if (seskey_len != n) + { + if (opt.verbose) + log_info ("%s: session key length %zu" + " does not match the length for algo %d\n", + __func__, seskey_len, seskey_algo); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (seskey, seskey_len, "seskey:"); + + enc_seskey_len = 1 + seskey_len + 8; + enc_seskey = xtrymalloc (enc_seskey_len); + if (!enc_seskey || enc_seskey_len > 254) + { + err = gpg_error_from_syserror (); + goto leave; + } + + enc_seskey[0] = enc_seskey_len - 1; + err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, + seskey, seskey_len); + if (err) + { + log_error ("%s: wrapping session key failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); + + resarr[0] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); + if (resarr[0]) + resarr[1] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); + if (resarr[1]) + resarr[2] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); + if (!resarr[0] || !resarr[1] || !resarr[2]) + { + err = gpg_error_from_syserror (); + for (i=0; i < 3; i++) + gcry_mpi_release (resarr[i]), resarr[i] = NULL; + } + + leave: + wipememory (ecc_ct, sizeof ecc_ct); + wipememory (ecc_ecdh, sizeof ecc_ecdh); + wipememory (ecc_ss, sizeof ecc_ss); + wipememory (kyber_ct, sizeof kyber_ct); + wipememory (kyber_ss, sizeof kyber_ss); + wipememory (kek, kek_len); + xfree (enc_seskey); + gcry_cipher_close (hd); + xfree (ecc_oid); + return err; +} + + +/* Core of the encryption for the ECDH algorithms. See pk_decrypt for + * a description of the arguments. */ +static gpg_error_t +do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) +{ + gcry_mpi_t *pkey = pk->pkey; + gcry_sexp_t s_ciph = NULL; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_pkey = NULL; + gpg_error_t err; + gcry_mpi_t k = NULL; + char *curve = NULL; + int with_djb_tweak_flag; + gcry_mpi_t public = NULL; + gcry_mpi_t result = NULL; + byte fp[MAX_FINGERPRINT_LEN]; + byte *shared = NULL; + byte *p; + size_t nshared; + unsigned int nbits; + + err = pk_ecdh_generate_ephemeral_key (pkey, &k); + if (err) + goto leave; + + curve = openpgp_oid_to_str (pkey[0]); + if (!curve) + { + err = gpg_error_from_syserror (); + goto leave; + } + + with_djb_tweak_flag = openpgp_oid_is_cv25519 (pkey[0]); + + /* Now use the ephemeral secret to compute the shared point. */ + err = gcry_sexp_build (&s_pkey, NULL, + with_djb_tweak_flag ? + "(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))" + : "(public-key(ecdh(curve%s)(q%m)))", + curve, pkey[1]); + if (err) + goto leave; + + /* Put K into a simplified S-expression. */ + err = gcry_sexp_build (&s_data, NULL, "%m", k); + if (err) + goto leave; + + /* Run encryption. */ + err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + if (err) + goto leave; + + gcry_sexp_release (s_data); s_data = NULL; + gcry_sexp_release (s_pkey); s_pkey = NULL; + + + /* Get the shared point and the ephemeral public key. */ + shared = get_data_from_sexp (s_ciph, "s", &nshared); + if (!shared) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = sexp_extract_param_sos (s_ciph, "e", &public); + gcry_sexp_release (s_ciph); s_ciph = NULL; + if (DBG_CRYPTO) + { + log_debug ("ECDH ephemeral key:"); + gcry_mpi_dump (public); + log_printf ("\n"); + } + + fingerprint_from_pk (pk, fp, NULL); + + p = gcry_mpi_get_opaque (data, &nbits); + result = NULL; + err = pk_ecdh_encrypt_with_shared_point (shared, nshared, fp, p, + (nbits+7)/8, pkey, &result); + if (err) + goto leave; + + resarr[0] = public; public = NULL; + resarr[1] = result; result = NULL; + leave: + gcry_mpi_release (public); + gcry_mpi_release (result); + xfree (shared); gcry_sexp_release (s_ciph); - return rc; + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + xfree (curve); + gcry_mpi_release (k); + return err; +} + + +/* Core of the encryption for RSA and Elgamal algorithms. See + * pk_decrypt for a description of the arguments. */ +static gpg_error_t +do_encrypt_rsa_elg (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) +{ + pubkey_algo_t algo = pk->pubkey_algo; + gcry_mpi_t *pkey = pk->pkey; + gcry_sexp_t s_ciph = NULL; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_pkey = NULL; + gpg_error_t err; + + if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(elg(p%m)(g%m)(y%m)))", + pkey[0], pkey[1], pkey[2]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(rsa(n%m)(e%m)))", + pkey[0], pkey[1]); + if (err) + goto leave; + + err = gcry_sexp_build (&s_data, NULL, "%m", data); + if (err) + goto leave; + + err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + if (err) + goto leave; + + gcry_sexp_release (s_data); s_data = NULL; + gcry_sexp_release (s_pkey); s_pkey = NULL; + + resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); + if (!is_RSA (algo)) + resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); + + leave: + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_ciph); + return err; +} + + +/* + * Emulate our old PK interface here - sometime in the future we might + * change the internal design to directly fit to libgcrypt. PK is is + * the OpenPGP public key packet, DATA is an MPI with the to be + * encrypted data, and RESARR receives the encrypted data. RESARRAY + * is expected to be an two item array which will be filled with newly + * allocated MPIs. SESKEY_ALGO is required for public key algorithms + * which do not encode it in DATA. + */ +gpg_error_t +pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr) +{ + pubkey_algo_t algo = pk->pubkey_algo; + + if (algo == PUBKEY_ALGO_KYBER) + return do_encrypt_kem (pk, data, seskey_algo, resarr); + else if (algo == PUBKEY_ALGO_ECDH) + return do_encrypt_ecdh (pk, data, resarr); + else if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) + return do_encrypt_rsa_elg (pk, data, resarr); + else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) + return do_encrypt_rsa_elg (pk, data, resarr); + else + return gpg_error (GPG_ERR_PUBKEY_ALGO); } diff --git a/g10/pkglue.h b/g10/pkglue.h index abeddb811..2b5c8b143 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -31,8 +31,8 @@ gpg_error_t sexp_extract_param_sos_nlz (gcry_sexp_t sexp, const char *param, int pk_verify (pubkey_algo_t algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey); -int pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, - PKT_public_key *pk, gcry_mpi_t *pkey); +gpg_error_t pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr); int pk_check_secret_key (pubkey_algo_t algo, gcry_mpi_t *skey); diff --git a/g10/plaintext.c b/g10/plaintext.c index 5c21dd7f6..a96214994 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -111,20 +111,20 @@ get_output_file (const byte *embedded_name, int embedded_namelen, { /* Special file name, no filename, or "-" given; write to the * file descriptor or to stdout. */ - int fd; + gnupg_fd_t fd; char xname[64]; - fd = check_special_filename (fname, 1, 0); - if (fd == -1) + fd = gnupg_check_special_filename (fname); + if (fd == GNUPG_INVALID_FD) { /* Not a special filename, thus we want stdout. */ fp = es_stdout; es_set_binary (fp); } - else if (!(fp = es_fdopen_nc (fd, "wb"))) + else if (!(fp = open_stream_nc (fd, "wb"))) { err = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); goto leave; } @@ -137,8 +137,7 @@ get_output_file (const byte *embedded_name, int embedded_namelen, if (!tmp || !*tmp) { xfree (tmp); - /* FIXME: Below used to be GPG_ERR_CREATE_FILE */ - err = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_EEXIST); goto leave; } xfree (fname); @@ -146,13 +145,7 @@ get_output_file (const byte *embedded_name, int embedded_namelen, } } - if (opt.outfp && is_secured_file (es_fileno (opt.outfp))) - { - err = gpg_error (GPG_ERR_EPERM); - log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err)); - goto leave; - } - else if (fp || nooutput) + if (fp || nooutput) ; else if (is_secured_filename (fname)) { @@ -729,8 +722,8 @@ hash_datafiles (gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, /* Hash the data from file descriptor DATA_FD and append the hash to hash contexts MD and MD2. */ int -hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, - int textmode) +hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, + gnupg_fd_t data_fd, int textmode) { progress_filter_context_t *pfx = new_progress_context (); iobuf_t fp; @@ -747,7 +740,7 @@ hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, { int rc = gpg_error_from_syserror (); log_error (_("can't open signed data fd=%d: %s\n"), - data_fd, strerror (errno)); + FD_DBG (data_fd), strerror (errno)); release_progress_context (pfx); return rc; } diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 6e1b0898e..563077803 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -117,6 +117,7 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) { if (!(k->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E || k->pubkey_algo == PUBKEY_ALGO_ECDH + || k->pubkey_algo == PUBKEY_ALGO_KYBER || k->pubkey_algo == PUBKEY_ALGO_RSA || k->pubkey_algo == PUBKEY_ALGO_RSA_E || k->pubkey_algo == PUBKEY_ALGO_ELGAMAL)) @@ -193,7 +194,7 @@ get_it (ctrl_t ctrl, { gpg_error_t err; byte *frame = NULL; - unsigned int n; + unsigned int frameidx; size_t nframe; u16 csum, csum2; int padding; @@ -237,6 +238,32 @@ get_it (ctrl_t ctrl, err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))", enc->data[1], enc->data[0]); } + else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + if ((opt.compat_flags & COMPAT_T7014_OLD)) + { + /* Temporary use for tests with original test vectors. */ + fixedinfo[0] = 0x69; + fixedlen = 1; + } + else + { + fixedinfo[0] = enc->seskey_algo; + v5_fingerprint_from_pk (sk, fixedinfo+1, NULL); + fixedlen = 33; + } + + if (!enc->data[0] || !enc->data[1] || !enc->data[2]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, + "(enc-val(pqc(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))", + enc->data[0], enc->data[1], enc->data[2], + enc->seskey_algo, fixedlen, fixedinfo); + } else err = gpg_error (GPG_ERR_BUG); @@ -248,6 +275,7 @@ get_it (ctrl_t ctrl, /* Decrypt. */ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); + err = agent_pkdecrypt (NULL, keygrip, desc, sk->keyid, sk->main_keyid, sk->pubkey_algo, s_data, &frame, &nframe, &padding); @@ -275,9 +303,22 @@ get_it (ctrl_t ctrl, */ if (DBG_CRYPTO) log_printhex (frame, nframe, "DEK frame:"); - n = 0; + frameidx = 0; - if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + /* We expect a 32 byte session key. We should not see this + * error here because due to the KEM mode the agent_pkdecrypt + * should have already failed. */ + if (nframe != 32) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + dek->keylen = nframe; + dek->algo = enc->seskey_algo; + } + else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t decoded; @@ -301,13 +342,21 @@ get_it (ctrl_t ctrl, goto leave; } nframe -= frame[nframe-1]; /* Remove padding. */ - log_assert (!n); /* (used just below) */ + if (4 > nframe) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + + dek->keylen = nframe - 3; + dek->algo = frame[0]; + frameidx = 1; } else { if (padding) { - if (n + 7 > nframe) + if (7 > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; @@ -319,34 +368,38 @@ get_it (ctrl_t ctrl, * using a Smartcard we are doing it the right way and * therefore we have to skip the zero. This should be fixed * in gpg-agent of course. */ - if (!frame[n]) - n++; + frameidx = 0; + if (!frame[frameidx]) + frameidx++; - if (frame[n] == 1 && frame[nframe - 1] == 2) + if (frame[frameidx] == 1 && frame[nframe - 1] == 2) { log_info (_("old encoding of the DEK is not supported\n")); err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } - if (frame[n] != 2) /* Something went wrong. */ + if (frame[frameidx] != 2) /* Something went wrong. */ { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } - for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ + /* Skip the random bytes. */ + for (frameidx++; frameidx < nframe && frame[frameidx]; frameidx++) ; - n++; /* Skip the zero byte. */ + frameidx++; /* Skip the zero byte. */ } - } - if (n + 4 > nframe) - { - err = gpg_error (GPG_ERR_WRONG_SECKEY); - goto leave; + if (frameidx + 4 > nframe) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + + dek->keylen = nframe - (frameidx + 1) - 2; + dek->algo = frame[frameidx++]; } - dek->keylen = nframe - (n + 1) - 2; - dek->algo = frame[n++]; + /* Check whether we support the ago. */ err = openpgp_cipher_test_algo (dek->algo); if (err) { @@ -365,16 +418,21 @@ get_it (ctrl_t ctrl, goto leave; } - /* Copy the key to DEK and compare the checksum. */ - csum = buf16_to_u16 (frame+nframe-2); - memcpy (dek->key, frame + n, dek->keylen); - for (csum2 = 0, n = 0; n < dek->keylen; n++) - csum2 += dek->key[n]; - if (csum != csum2) + /* Copy the key to DEK and compare the checksum if needed. */ + /* We use the frameidx as flag for the need of a checksum. */ + memcpy (dek->key, frame + frameidx, dek->keylen); + if (frameidx) { - err = gpg_error (GPG_ERR_WRONG_SECKEY); - goto leave; + csum = buf16_to_u16 (frame+nframe-2); + for (csum2 = 0, frameidx = 0; frameidx < dek->keylen; frameidx++) + csum2 += dek->key[frameidx]; + if (csum != csum2) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } } + if (DBG_CLOCK) log_clock ("decryption ready"); if (DBG_CRYPTO) @@ -399,6 +457,9 @@ get_it (ctrl_t ctrl, log_info (_("WARNING: cipher algorithm %s not found in recipient" " preferences\n"), openpgp_cipher_algo_name (dek->algo)); + /* if (!err && 25519 && openpgp_oidbuf_is_ed25519 (curve, len)) */ + /* log_info ("Note: legacy OID was used for cv25519\n"); */ + if (!err) { kbnode_t k; diff --git a/g10/revoke.c b/g10/revoke.c index d6cbf93cb..ef5bb4d78 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -333,7 +333,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) if( !opt.armor ) tty_printf(_("ASCII armored output forced.\n")); - if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, 1, &out )) ) goto leave; afx->what = 1; @@ -464,7 +464,7 @@ create_revocation (ctrl_t ctrl, afx = new_armor_context (); - if ((rc = open_outfile (-1, filename, suffix, 1, &out))) + if ((rc = open_outfile (GNUPG_INVALID_FD, filename, suffix, 1, &out))) goto leave; if (leadintext ) diff --git a/g10/server.c b/g10/server.c index 60b447c41..24e525e7f 100644 --- a/g10/server.c +++ b/g10/server.c @@ -265,7 +265,7 @@ cmd_encrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - int inp_fd, out_fd; + gnupg_fd_t inp_fd, out_fd; (void)line; /* LINE is not used. */ @@ -276,14 +276,14 @@ cmd_encrypt (assuan_context_t ctx, char *line) goto leave; } - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) { err = set_error (GPG_ERR_ASS_NO_INPUT, NULL); goto leave; } - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) { err = set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); goto leave; @@ -327,15 +327,15 @@ cmd_decrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - int inp_fd, out_fd; + gnupg_fd_t inp_fd, out_fd; (void)line; /* LINE is not used. */ - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); glo_ctrl.lasterr = 0; @@ -388,16 +388,7 @@ cmd_verify (assuan_context_t ctx, char *line) if (out_fd != GNUPG_INVALID_FD) { - es_syshd_t syshd; - -#ifdef HAVE_W32_SYSTEM - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = out_fd; -#else - syshd.type = ES_SYSHD_FD; - syshd.u.fd = out_fd; -#endif - out_fp = es_sysopen_nc (&syshd, "w"); + out_fp = open_stream_nc (fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } diff --git a/g10/seskey.c b/g10/seskey.c index 15c210b78..2fe8e9de7 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -86,6 +86,22 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) if (DBG_CRYPTO) log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); + if (openpgp_pk_algo == PUBKEY_ALGO_KYBER) + { + /* Straightforward encoding w/o extra checksum as used by ECDH. */ + nframe = dek->keylen; + log_assert (nframe > 4); /*(for the log_debug)*/ + frame = xmalloc_secure (nframe); + memcpy (frame, dek->key, nframe); + if (DBG_CRYPTO) + log_debug ("encode_session_key: " + "[%d] %02x %02x %02x ... %02x %02x %02x\n", + (int) dek->keylen, frame[0], frame[1], frame[2], + frame[nframe-3], frame[nframe-2], frame[nframe-1]); + + return gcry_mpi_set_opaque (NULL, frame, 8*nframe); + } + csum = 0; for (p = dek->key, i=0; i < dek->keylen; i++) csum += *p++; diff --git a/g10/sign.c b/g10/sign.c index b00bdfefd..67ea5a038 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -495,6 +495,7 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, gcry_sexp_t s_sigval; desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1); + /* FIXME: Eventually support dual keys. */ err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc, pksk->keyid, pksk->main_keyid, pksk->pubkey_algo, dp, gcry_md_get_algo_dlen (mdalgo), mdalgo, @@ -580,6 +581,7 @@ openpgp_card_v1_p (PKT_public_key *pk) { char *hexgrip; + /* Note: No need to care about dual keys for non-RSA keys. */ err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { @@ -1022,7 +1024,9 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, const char *fname; armor_filter_context_t *afx; compress_filter_context_t zfx; + gcry_md_hd_t md = NULL; md_filter_context_t mfx; + md_thd_filter_context_t mfx2 = NULL; text_filter_context_t tfx; progress_filter_context_t *pfx; encrypt_filter_context_t efx; @@ -1115,7 +1119,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, else if (opt.verbose) log_info (_("writing to '%s'\n"), outfile); } - else if ((rc = open_outfile (-1, fname, + else if ((rc = open_outfile (GNUPG_INVALID_FD, fname, opt.armor? 1 : detached? 2 : 0, 0, &out))) { goto leave; @@ -1128,10 +1132,10 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, iobuf_push_filter (inp, text_filter, &tfx); } - if (gcry_md_open (&mfx.md, 0, 0)) + if (gcry_md_open (&md, 0, 0)) BUG (); if (DBG_HASHING) - gcry_md_debug (mfx.md, "sign"); + gcry_md_debug (md, "sign"); /* If we're encrypting and signing, it is reasonable to pick the * hash algorithm to use out of the recipient key prefs. This is @@ -1228,10 +1232,21 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, } for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) - gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); + gcry_md_enable (md, hash_for (sk_rover->pk)); if (!multifile) - iobuf_push_filter (inp, md_filter, &mfx); + { + if (encryptflag && (opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } + } if (detached && !encryptflag) afx->what = 2; @@ -1294,7 +1309,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; } - write_status_begin_signing (mfx.md); + write_status_begin_signing (md); /* Setup the inner packet. */ if (detached) @@ -1334,7 +1349,16 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, memset (&tfx, 0, sizeof tfx); iobuf_push_filter (inp, text_filter, &tfx); } - iobuf_push_filter (inp, md_filter, &mfx); + if (encryptflag && (opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } while (iobuf_read (inp, NULL, iobuf_size) != -1) ; iobuf_close (inp); @@ -1363,7 +1387,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; /* Write the signatures. */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, + rc = write_signature_packets (ctrl, sk_list, out, md, extrahash, opt.textmode && !outfile? 0x01 : 0x00, 0, duration, detached ? 'D':'S', NULL); if (rc) @@ -1380,7 +1404,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, write_status (STATUS_END_ENCRYPTION); } iobuf_close (inp); - gcry_md_close (mfx.md); + gcry_md_close (md); release_sk_list (sk_list); release_pk_list (pk_list); recipient_digest_algo = 0; @@ -1461,7 +1485,7 @@ clearsign_file (ctrl_t ctrl, log_info (_("writing to '%s'\n"), outfile); } - else if ((rc = open_outfile (-1, fname, 1, 0, &out))) + else if ((rc = open_outfile (GNUPG_INVALID_FD, fname, 1, 0, &out))) { goto leave; } @@ -1563,6 +1587,8 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) progress_filter_context_t *pfx; compress_filter_context_t zfx; md_filter_context_t mfx; + md_thd_filter_context_t mfx2 = NULL; + gcry_md_hd_t md = NULL; text_filter_context_t tfx; cipher_filter_context_t cfx; iobuf_t inp = NULL; @@ -1639,22 +1665,32 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) /**/ : "CFB"); /* Now create the outfile. */ - rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out); + rc = open_outfile (GNUPG_INVALID_FD, fname, opt.armor? 1:0, 0, &out); if (rc) goto leave; /* Prepare to calculate the MD over the input. */ if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); - if (gcry_md_open (&mfx.md, 0, 0)) + if (gcry_md_open (&md, 0, 0)) BUG (); if (DBG_HASHING) - gcry_md_debug (mfx.md, "symc-sign"); + gcry_md_debug (md, "symc-sign"); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) - gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); + gcry_md_enable (md, hash_for (sk_rover->pk)); + + if ((opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } - iobuf_push_filter (inp, md_filter, &mfx); /* Push armor output filter */ if (opt.armor) @@ -1696,7 +1732,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) if (rc) goto leave; - write_status_begin_signing (mfx.md); + write_status_begin_signing (md); /* Pipe data through all filters; i.e. write the signed stuff. */ /* (current filters: zip - encrypt - armor) */ @@ -1708,7 +1744,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) /* Write the signatures. */ /* (current filters: zip - encrypt - armor) */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, + rc = write_signature_packets (ctrl, sk_list, out, md, extrahash, opt.textmode? 0x01 : 0x00, 0, duration, 'S', NULL); if (rc) @@ -1725,7 +1761,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) } iobuf_close (inp); release_sk_list (sk_list); - gcry_md_close (mfx.md); + gcry_md_close (md); xfree (cfx.dek); xfree (s2k); release_progress_context (pfx); diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 058ab5cf6..99f135678 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -141,19 +141,16 @@ import_ownertrust (ctrl_t ctrl, const char *fname ) fname = "[stdin]"; is_stdin = 1; } + else if (is_secured_filename (fname)) { + gpg_err_set_errno (EPERM); + log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); + return; + } else if( !(fp = es_fopen( fname, "r" )) ) { log_error ( _("can't open '%s': %s\n"), fname, strerror(errno) ); return; } - if (is_secured_file (es_fileno (fp))) - { - es_fclose (fp); - gpg_err_set_errno (EPERM); - log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); - return; - } - while (es_fgets (line, DIM(line)-1, fp)) { TRUSTREC rec; diff --git a/g10/verify.c b/g10/verify.c index 1c3de767c..c2c63255c 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -240,7 +240,8 @@ verify_files (ctrl_t ctrl, int nfiles, char **files ) FIXME: OUTFP is not yet implemented. */ int -gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp) +gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd, + estream_t out_fp) { int rc; iobuf_t fp; @@ -260,7 +261,8 @@ gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp) if (!fp) { rc = gpg_error_from_syserror (); - log_error (_("can't open fd %d: %s\n"), sig_fd, strerror (errno)); + log_error (_("can't open fd %d: %s\n"), FD_DBG (sig_fd), + strerror (errno)); goto leave; } diff --git a/g13/be-encfs.c b/g13/be-encfs.c index 0e2c68bf3..ac6d6d6cd 100644 --- a/g13/be-encfs.c +++ b/g13/be-encfs.c @@ -28,10 +28,10 @@ #include "g13.h" #include "../common/i18n.h" #include "keyblob.h" -#include "be-encfs.h" -#include "runner.h" #include "../common/sysutils.h" #include "../common/exechelp.h" +#include "runner.h" +#include "be-encfs.h" /* Command values used to run the encfs tool. */ @@ -81,7 +81,9 @@ run_umount_helper (const char *mountpoint) args[1] = mountpoint; args[2] = NULL; - err = gnupg_spawn_process_detached (pgmname, args, NULL); + err = gnupg_process_spawn (pgmname, args, + GNUPG_PROCESS_DETACHED, + NULL, NULL, NULL); if (err) log_error ("failed to run '%s': %s\n", pgmname, gpg_strerror (err)); @@ -218,12 +220,11 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, gpg_error_t err; encfs_parm_t parm; runner_t runner = NULL; - int outbound[2] = { -1, -1 }; - int inbound[2] = { -1, -1 }; const char *pgmname; const char *argv[10]; - pid_t pid = (pid_t)(-1); int idx; + gnupg_process_t proc; + int inbound, outbound; (void)ctrl; @@ -246,15 +247,6 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, if (err) goto leave; - err = gnupg_create_inbound_pipe (inbound, NULL, 0); - if (!err) - err = gnupg_create_outbound_pipe (outbound, NULL, 0); - if (err) - { - log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); - goto leave; - } - pgmname = ENCFS; idx = 0; argv[idx++] = "-f"; @@ -267,47 +259,42 @@ run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd, argv[idx++] = NULL; assert (idx <= DIM (argv)); - err = gnupg_spawn_process_fd (pgmname, argv, - outbound[0], -1, inbound[1], &pid); + err = gnupg_process_spawn (pgmname, argv, + (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDERR_PIPE), + NULL, NULL, &proc); if (err) { log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err)); goto leave; } - close (outbound[0]); outbound[0] = -1; - close ( inbound[1]); inbound[1] = -1; - runner_set_fds (runner, inbound[0], outbound[1]); - inbound[0] = -1; /* Now owned by RUNNER. */ - outbound[1] = -1; /* Now owned by RUNNER. */ + err = gnupg_process_get_fds (proc, 0, &outbound, NULL, &inbound); + if (err) + { + log_error ("error get fds '%s': %s\n", pgmname, gpg_strerror (err)); + gnupg_process_release (proc); + goto leave; + } + + runner_set_fds (runner, inbound, outbound); runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm); parm = NULL; /* Now owned by RUNNER. */ - runner_set_pid (runner, pid); - pid = (pid_t)(-1); /* The process is now owned by RUNNER. */ + runner_set_proc (runner, proc); err = runner_spawn (runner); if (err) - goto leave; + { + gnupg_process_release (proc); + goto leave; + } *r_id = runner_get_rid (runner); log_info ("running '%s' in the background\n", pgmname); leave: - if (inbound[0] != -1) - close (inbound[0]); - if (inbound[1] != -1) - close (inbound[1]); - if (outbound[0] != -1) - close (outbound[0]); - if (outbound[1] != -1) - close (outbound[1]); - if (pid != (pid_t)(-1)) - { - gnupg_wait_process (pgmname, pid, 1, NULL); - gnupg_release_process (pid); - } runner_release (runner); encfs_handler_cleanup (parm); return err; diff --git a/g13/g13-syshelp.c b/g13/g13-syshelp.c index 0de1cf15d..08b67377c 100644 --- a/g13/g13-syshelp.c +++ b/g13/g13-syshelp.c @@ -306,7 +306,6 @@ main (int argc, char **argv) /* Prepare libassuan. */ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); - /*assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);*/ setup_libassuan_logging (&opt.debug, NULL); /* Setup a default control structure for command line mode. */ @@ -40,6 +40,7 @@ #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "../common/init.h" +#include "../common/exechelp.h" #include "keyblob.h" #include "server.h" #include "runner.h" @@ -228,10 +229,6 @@ static void start_idle_task (void); static void join_idle_task (void); -/* Begin NPth wrapper functions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - - static const char * my_strusage( int level ) { @@ -378,6 +375,7 @@ main (int argc, char **argv) init_common_subsystems (&argc, &argv); npth_init (); + gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); /* Take extra care of the random pool. */ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); @@ -436,8 +434,8 @@ main (int argc, char **argv) /* Prepare libassuan. */ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); setup_libassuan_logging (&opt.debug, NULL); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* Setup a default control structure for command line mode. */ memset (&ctrl, 0, sizeof ctrl); diff --git a/g13/mount.c b/g13/mount.c index 45b60806c..071b76b67 100644 --- a/g13/mount.c +++ b/g13/mount.c @@ -34,10 +34,11 @@ #include "backend.h" #include "g13tuple.h" #include "mountinfo.h" -#include "runner.h" #include "../common/host2net.h" #include "server.h" /*(g13_keyblob_decrypt)*/ #include "../common/sysutils.h" +#include "../common/exechelp.h" +#include "runner.h" #include "call-syshelp.h" diff --git a/g13/runner.c b/g13/runner.c index b08d99030..c0534fe5d 100644 --- a/g13/runner.c +++ b/g13/runner.c @@ -29,8 +29,8 @@ #include "g13.h" #include "../common/i18n.h" #include "keyblob.h" -#include "runner.h" #include "../common/exechelp.h" +#include "runner.h" #include "mountinfo.h" /* The runner object. */ @@ -55,7 +55,7 @@ struct runner_s 2 = Thread is running and someone is holding a reference. */ int refcount; - pid_t pid; /* PID of the backend's process (the engine). */ + gnupg_process_t proc; /* Process of the backend's process (the engine). */ int in_fd; /* File descriptors to read from the engine. */ int out_fd; /* File descriptors to write to the engine. */ engine_handler_fnc_t handler; /* The handler functions. */ @@ -157,16 +157,16 @@ runner_release (runner_t runner) if (runner->handler_cleanup) runner->handler_cleanup (runner->handler_data); - if (runner->pid != (pid_t)(-1)) + if (runner->proc) { /* The process has not been cleaned up - do it now. */ - gnupg_kill_process (runner->pid); + gnupg_process_terminate (runner->proc); /* (Actually we should use the program name and not the arbitrary NAME of the runner object. However it does not matter because that information is only used for diagnostics.) */ - gnupg_wait_process (runner->name, runner->pid, 1, NULL); - gnupg_release_process (runner->pid); + gnupg_process_wait (runner->proc, 1); + gnupg_process_release (runner->proc); } xfree (runner->name); @@ -212,7 +212,7 @@ runner_new (runner_t *r_runner, const char *name) return gpg_error_from_syserror (); } runner->refcount = 1; - runner->pid = (pid_t)(-1); + runner->proc = NULL; runner->in_fd = -1; runner->out_fd = -1; @@ -266,15 +266,15 @@ runner_set_fds (runner_t runner, int in_fd, int out_fd) } -/* Set the PID of the backend engine. After this call the engine is +/* Set the PROC of the backend engine. After this call the engine is owned by the runner object. */ void -runner_set_pid (runner_t runner, pid_t pid) +runner_set_proc (runner_t runner, gnupg_process_t proc) { - if (check_already_spawned (runner, "runner_set_fds")) + if (check_already_spawned (runner, "runner_set_proc")) return; - runner->pid = pid; + runner->proc = proc; } @@ -366,15 +366,17 @@ runner_thread (void *arg) } /* Now wait for the process to finish. */ - if (!err && runner->pid != (pid_t)(-1)) + if (!err && runner->proc) { int exitcode; log_debug ("runner thread waiting ...\n"); - err = gnupg_wait_process (runner->name, runner->pid, 1, &exitcode); - gnupg_release_process (runner->pid); - runner->pid = (pid_t)(-1); - if (err) + err = gnupg_process_wait (runner->proc, 1); + if (!err) + gnupg_process_ctl (runner->proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + gnupg_process_release (runner->proc); + runner->proc = NULL; + if (exitcode) log_error ("running '%s' failed (exitcode=%d): %s\n", runner->name, exitcode, gpg_strerror (err)); log_debug ("runner thread waiting finished\n"); @@ -473,7 +475,7 @@ runner_cancel (runner_t runner) need to change the thread to wait on an event. */ runner->cancel_flag = 1; /* For now we use the brutal way and kill the process. */ - gnupg_kill_process (runner->pid); + gnupg_process_terminate (runner->proc); } } diff --git a/g13/runner.h b/g13/runner.h index 36181adf9..01c395e02 100644 --- a/g13/runner.h +++ b/g13/runner.h @@ -49,7 +49,7 @@ runner_t runner_find_by_rid (unsigned int rid); /* Functions to set properties of the runner. */ void runner_set_fds (runner_t runner, int in_fd, int out_fd); -void runner_set_pid (runner_t runner, pid_t pid); +void runner_set_proc (runner_t runner, gnupg_process_t proc); /* Register the handler functions with a runner. */ void runner_set_handler (runner_t runner, diff --git a/kbx/kbx-client-util.c b/kbx/kbx-client-util.c index f6456a508..b900586c8 100644 --- a/kbx/kbx-client-util.c +++ b/kbx/kbx-client-util.c @@ -53,6 +53,7 @@ struct kbx_client_data_s /* Condition variable to sync the datastream with the command. */ npth_mutex_t mutex; npth_cond_t cond; + npth_t thd; /* The data received from the keyboxd and an error code if there was * a problem (in which case DATA is also set to NULL. This is only @@ -103,7 +104,6 @@ prepare_data_pipe (kbx_client_data_t kcd) int rc; int inpipe[2]; estream_t infp; - npth_t thread; npth_attr_t tattr; kcd->fp = NULL; @@ -118,14 +118,18 @@ prepare_data_pipe (kbx_client_data_t kcd) return err; /* That should not happen. */ } - err = assuan_sendfd (kcd->ctx, INT2FD (inpipe[1])); +#ifdef HAVE_W32_SYSTEM + err = assuan_sendfd (kcd->ctx, (HANDLE)_get_osfhandle (inpipe[1])); +#else + err = assuan_sendfd (kcd->ctx, inpipe[1]); +#endif if (err) { - log_error ("sending sending fd %d to keyboxd: %s <%s>\n", + log_error ("sending fd %d to keyboxd: %s <%s>\n", inpipe[1], gpg_strerror (err), gpg_strsource (err)); es_fclose (infp); gnupg_close_pipe (inpipe[1]); - return 0; /* Server may not support fd-passing. */ + return err; } err = assuan_transact (kcd->ctx, "OUTPUT FD", @@ -135,12 +139,12 @@ prepare_data_pipe (kbx_client_data_t kcd) log_info ("keyboxd does not accept our fd: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); es_fclose (infp); - return 0; + return err; } + close (inpipe[1]); kcd->fp = infp; - rc = npth_attr_init (&tattr); if (rc) { @@ -150,8 +154,8 @@ prepare_data_pipe (kbx_client_data_t kcd) kcd->fp = NULL; return err; } - npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); - rc = npth_create (&thread, &tattr, datastream_thread, kcd); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + rc = npth_create (&kcd->thd, &tattr, datastream_thread, kcd); if (rc) { err = gpg_error_from_errno (rc); @@ -162,6 +166,7 @@ prepare_data_pipe (kbx_client_data_t kcd) return err; } + npth_attr_destroy (&tattr); return 0; } @@ -193,13 +198,8 @@ datastream_thread (void *arg) gnupg_sleep (1); continue; } - if (nread != 4) - { - err = gpg_error (GPG_ERR_EIO); - log_error ("error reading data length from keyboxd: %s\n", - "short read"); - continue; - } + if (nread < 4) + break; datalen = buf32_to_size_t (lenbuf); /* log_debug ("keyboxd announced %zu bytes\n", datalen); */ @@ -294,8 +294,7 @@ kbx_client_data_new (kbx_client_data_t *r_kcd, assuan_context_t ctx, { err = gpg_error_from_errno (rc); log_error ("error initializing mutex: %s\n", gpg_strerror (err)); - xfree (kcd); - return err; + goto leave; /* Use D-lines. */ } rc = npth_cond_init (&kcd->cond, NULL); if (rc) @@ -303,8 +302,7 @@ kbx_client_data_new (kbx_client_data_t *r_kcd, assuan_context_t ctx, err = gpg_error_from_errno (rc); log_error ("error initializing condition: %s\n", gpg_strerror (err)); npth_mutex_destroy (&kcd->mutex); - xfree (kcd); - return err; + goto leave; /* Use D-lines. */ } err = prepare_data_pipe (kcd); @@ -312,8 +310,7 @@ kbx_client_data_new (kbx_client_data_t *r_kcd, assuan_context_t ctx, { npth_cond_destroy (&kcd->cond); npth_mutex_destroy (&kcd->mutex); - xfree (kcd); - return err; + /* Use D-lines. */ } leave: @@ -329,11 +326,20 @@ kbx_client_data_release (kbx_client_data_t kcd) if (!kcd) return; + fp = kcd->fp; + if (!fp) + { + xfree (kcd); + return; + } + + if (npth_join (kcd->thd, NULL)) + log_error ("kbx_client_data_release failed on npth_join"); + kcd->fp = NULL; - es_fclose (fp); /* That close should let the thread run into an error. */ - /* FIXME: Make thread killing explicit. Otherwise we run in a - * log_fatal due to the destroyed mutex. */ + es_fclose (fp); + npth_cond_destroy (&kcd->cond); npth_mutex_destroy (&kcd->mutex); xfree (kcd); diff --git a/kbx/kbxserver.c b/kbx/kbxserver.c index ae9ae5c75..d09a8f8eb 100644 --- a/kbx/kbxserver.c +++ b/kbx/kbxserver.c @@ -131,21 +131,25 @@ get_assuan_ctx_from_ctrl (ctrl_t ctrl) static gpg_error_t prepare_outstream (ctrl_t ctrl) { - int fd; + gnupg_fd_t fd; + estream_t out_fp = NULL; log_assert (ctrl && ctrl->server_local); if (ctrl->server_local->outstream) return 0; /* Already enabled. */ - fd = translate_sys2libc_fd - (assuan_get_output_fd (get_assuan_ctx_from_ctrl (ctrl)), 1); - if (fd == -1) + fd = assuan_get_output_fd (get_assuan_ctx_from_ctrl (ctrl)); + if (fd == GNUPG_INVALID_FD) return 0; /* No Output command active. */ + else + { + out_fp = open_stream_nc (fd, "w"); + if (!out_fp) + return gpg_err_code_from_syserror (); + } - ctrl->server_local->outstream = es_fdopen_nc (fd, "w"); - if (!ctrl->server_local->outstream) - return gpg_err_code_from_syserror (); + ctrl->server_local->outstream = out_fp; return 0; } @@ -946,15 +950,9 @@ kbxd_start_command_handler (ctrl_t ctrl, gnupg_fd_t fd, unsigned int session_id) } else { - /* The fd-passing does not work reliable on Windows, and even it - * it is not used by gpg and gpgsm the current libassuan slows - * down things if it is allowed for the server.*/ rc = assuan_init_socket_server (ctx, fd, (ASSUAN_SOCKET_SERVER_ACCEPTED -#ifndef HAVE_W32_SYSTEM - |ASSUAN_SOCKET_SERVER_FDPASSING -#endif - )); + |ASSUAN_SOCKET_SERVER_FDPASSING)); } if (rc) diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index 935dbc735..c7ac5b852 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -132,7 +132,6 @@ my_strusage( int level ) } - /* static void */ /* wrong_args( const char *text ) */ /* { */ diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c index 2564d1f48..7d0670d9f 100644 --- a/kbx/keybox-blob.c +++ b/kbx/keybox-blob.c @@ -96,7 +96,7 @@ bit 0 = qualified signature (not yet implemented} bit 7 = 32 byte fingerprint in use. - u16 RFU - - b20 keygrip + - b20 keygrip FIXME: Support a second grip. - bN Optional filler up to the specified length of this structure. - u16 Size of the serial number (may be zero) diff --git a/kbx/keybox-openpgp.c b/kbx/keybox-openpgp.c index f5bd1b641..c91885b32 100644 --- a/kbx/keybox-openpgp.c +++ b/kbx/keybox-openpgp.c @@ -233,6 +233,26 @@ keygrip_from_keyparm (int algo, struct keyparm_s *kp, unsigned char *grip) } break; + case PUBKEY_ALGO_KYBER: + /* There is no space in the BLOB for a second grip, thus for now + * we store only the ECC keygrip. */ + { + char *curve = openpgp_oidbuf_to_str (kp[0].mpi, kp[0].len); + if (!curve) + err = gpg_error_from_syserror (); + else + { + err = gcry_sexp_build + (&s_pkey, NULL, + openpgp_oidbuf_is_cv25519 (kp[0].mpi, kp[0].len) + ?"(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))" + : "(public-key(ecc(curve%s)(q%b)))", + curve, kp[1].len, kp[1].mpi); + xfree (curve); + } + } + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -273,6 +293,7 @@ parse_key (const unsigned char *data, size_t datalen, unsigned char hashbuffer[768]; gcry_md_hd_t md; int is_ecc = 0; + int is_kyber = 0; int is_v5; /* unsigned int pkbytes; for v5: # of octets of the public key params. */ struct keyparm_s keyparm[OPENPGP_MAX_NPKEY]; @@ -331,6 +352,10 @@ parse_key (const unsigned char *data, size_t datalen, npkey = 2; is_ecc = 1; break; + case PUBKEY_ALGO_KYBER: + npkey = 3; + is_kyber = 1; + break; default: /* Unknown algorithm. */ return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); } @@ -345,7 +370,8 @@ parse_key (const unsigned char *data, size_t datalen, if (datalen < 2) return gpg_error (GPG_ERR_INV_PACKET); - if (is_ecc && (i == 0 || i == 2)) + if ((is_ecc && (i == 0 || i == 2)) + || (is_kyber && i == 0 )) { nbytes = data[0]; if (nbytes < 2 || nbytes > 254) @@ -357,6 +383,20 @@ parse_key (const unsigned char *data, size_t datalen, keyparm[i].mpi = data; keyparm[i].len = nbytes; } + else if (is_kyber && i == 2) + { + if (datalen < 4) + return gpg_error (GPG_ERR_INV_PACKET); + nbytes = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3])); + data += 4; + datalen -= 4; + /* (for the limit see also MAX_EXTERN_MPI_BITS in g10/gpg.h) */ + if (datalen < nbytes || nbytes > (32768*8)) + return gpg_error (GPG_ERR_INV_PACKET); + + keyparm[i].mpi = data; + keyparm[i].len = nbytes; + } else { nbits = ((data[0]<<8)|(data[1])); @@ -378,7 +418,7 @@ parse_key (const unsigned char *data, size_t datalen, /* Note: Starting here we need to jump to leave on error. */ /* For non-ECC, make sure the MPIs are unsigned. */ - if (!is_ecc) + if (!is_ecc && !is_kyber) for (i=0; i < npkey; i++) { if (!keyparm[i].len || (keyparm[i].mpi[0] & 0x80)) @@ -438,7 +478,7 @@ parse_key (const unsigned char *data, size_t datalen, */ if (version == 5) { - if ( 5 + n < sizeof hashbuffer ) + if (5 + n < sizeof hashbuffer ) { hashbuffer[0] = 0x9a; /* CTB */ hashbuffer[1] = (n >> 24);/* 4 byte length header. */ diff --git a/kbx/keybox-search.c b/kbx/keybox-search.c index 31ea0ba60..303c19b79 100644 --- a/kbx/keybox-search.c +++ b/kbx/keybox-search.c @@ -279,7 +279,8 @@ blob_cmp_fpr (KEYBOXBLOB blob, const unsigned char *fpr, unsigned int fprlen) } -/* Helper for has_short_kid and has_long_kid. */ +/* Helper for has_short_kid and has_long_kid. This function is called + * with FPROFF 12 and FPRLEN 4 or with FPROFF 12 and FPRLEN 8. */ static int blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, int fproff, int fprlen) @@ -288,7 +289,8 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, size_t length; size_t pos, off; size_t nkeys, keyinfolen; - int idx, fpr32, storedfprlen; + int idx; + int fpr32; /* Set if this blob stores all fingerprints as 32 bytes. */ buffer = _keybox_get_blob_image (blob, &length); if (length < 40) @@ -307,13 +309,24 @@ blob_cmp_fpr_part (KEYBOXBLOB blob, const unsigned char *fpr, for (idx=0; idx < nkeys; idx++) { off = pos + idx*keyinfolen; - if (fpr32) - storedfprlen = (get16 (buffer + off + 32) & 0x80)? 32:20; + if (!fpr32) + { + /* Blob has only 20 fingerprints - use the FPROFF. */ + if (!memcmp (buffer + off + fproff, fpr, fprlen)) + return idx+1; /* found */ + } + else if ((buffer[off + 32 + 1] & 0x80)) + { + /* This (sub)key has a 32 byte fpr -> use 0 as offset. */ + if (!memcmp (buffer + off, fpr, fprlen)) + return idx+1; /* found */ + } else - storedfprlen = 20; - if ((fpr32 || storedfprlen == fproff + fprlen) - && !memcmp (buffer + off + fproff, fpr, fprlen)) - return idx+1; /* found */ + { + /* This (sub)key has a 20 byte fpr -> use the FPROFF */ + if (!memcmp (buffer + off + fproff, fpr, fprlen)) + return idx+1; /* found */ + } } return 0; /* not found */ } @@ -683,43 +696,30 @@ blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip) static inline int has_short_kid (KEYBOXBLOB blob, u32 lkid) { - const unsigned char *buffer; size_t length; - int fpr32; unsigned char buf[4]; - buffer = _keybox_get_blob_image (blob, &length); + _keybox_get_blob_image (blob, &length); if (length < 48) return 0; /* blob too short */ - fpr32 = buffer[5] == 2; - if (fpr32 && length < 56) - return 0; /* blob to short */ buf[0] = lkid >> 24; buf[1] = lkid >> 16; buf[2] = lkid >> 8; buf[3] = lkid; - if (fpr32 && (get16 (buffer + 20 + 32) & 0x80)) - return blob_cmp_fpr_part (blob, buf, 0, 4); - else - return blob_cmp_fpr_part (blob, buf, 16, 4); + return blob_cmp_fpr_part (blob, buf, 16, 4); } static inline int has_long_kid (KEYBOXBLOB blob, u32 mkid, u32 lkid) { - const unsigned char *buffer; size_t length; - int fpr32; unsigned char buf[8]; - buffer = _keybox_get_blob_image (blob, &length); + _keybox_get_blob_image (blob, &length); if (length < 48) return 0; /* blob too short */ - fpr32 = buffer[5] == 2; - if (fpr32 && length < 56) - return 0; /* blob to short */ buf[0] = mkid >> 24; buf[1] = mkid >> 16; @@ -730,10 +730,7 @@ has_long_kid (KEYBOXBLOB blob, u32 mkid, u32 lkid) buf[6] = lkid >> 8; buf[7] = lkid; - if (fpr32 && (get16 (buffer + 20 + 32) & 0x80)) - return blob_cmp_fpr_part (blob, buf, 0, 8); - else - return blob_cmp_fpr_part (blob, buf, 12, 8); + return blob_cmp_fpr_part (blob, buf, 12, 8); } static inline int diff --git a/kbx/keyboxd.c b/kbx/keyboxd.c index f875e115d..73905eb7d 100644 --- a/kbx/keyboxd.c +++ b/kbx/keyboxd.c @@ -144,14 +144,13 @@ static struct debug_flags_s debug_flags [] = { 77, NULL } /* 77 := Do not exit on "help" or "?". */ }; -/* The timer tick used for housekeeping stuff. Note that on Windows - * we use a SetWaitableTimer seems to signal earlier than about 2 - * seconds. Thus we use 4 seconds on all platforms. - * CHECK_OWN_SOCKET_INTERVAL defines how often we check - * our own socket in standard socket mode. If that value is 0 we - * don't check at all. All values are in seconds. */ -# define TIMERTICK_INTERVAL (4) -# define CHECK_OWN_SOCKET_INTERVAL (60) +/* CHECK_OWN_SOCKET_INTERVAL defines how often we check our own socket + * in standard socket mode. If that value is 0 we don't check at all. + * Values is in seconds. */ +#define CHECK_OWN_SOCKET_INTERVAL (60) +/* CHECK_PROBLEMS_INTERVAL defines how often we check the existence of + * homedir. Value is in seconds. */ +#define CHECK_PROBLEMS_INTERVAL (4) /* The list of open file descriptors at startup. Note that this list * has been allocated using the standard malloc. */ @@ -171,8 +170,10 @@ static int shutdown_pending; /* Flag indicating to start the daemon even if one already runs. */ static int steal_socket; -/* Counter for the currently running own socket checks. */ -static int check_own_socket_running; +/* Flag to monitor problems. */ +static int problem_detected; +#define KEYBOXD_PROBLEM_SOCKET_TAKEOVER (1 << 0) +#define KEYBOXD_PROBLEM_HOMEDIR_REMOVED (1 << 1) /* Flag to indicate that we shall not watch our own socket. */ static int disable_check_own_socket; @@ -192,6 +193,17 @@ static assuan_sock_nonce_t socket_nonce; * Let's try this as default. Change at runtime with --listen-backlog. */ static int listen_backlog = 64; +#ifdef HAVE_W32_SYSTEM +/* The event to break the select call. */ +static HANDLE the_event2; +#elif defined(HAVE_PSELECT_NO_EINTR) +/* An FD to break the select call. */ +static int event_pipe_fd; +#else +/* PID of the main thread. */ +static pid_t main_thread_pid; +#endif + /* Name of a config file, which will be reread on a HUP if it is not NULL. */ static char *config_filename; @@ -199,16 +211,6 @@ static char *config_filename; * the log file after a SIGHUP if it didn't changed. Malloced. */ static char *current_logfile; -/* This flag is true if the inotify mechanism for detecting the - * removal of the homedir is active. This flag is used to disable the - * alternative but portable stat based check. */ -static int have_homedir_inotify; - -/* Depending on how keyboxd was started, the homedir inotify watch may - * not be reliable. This flag is set if we assume that inotify works - * reliable. */ -static int reliable_homedir_inotify; - /* Number of active connections. */ static int active_connections; @@ -255,12 +257,11 @@ static void kbxd_init_default_ctrl (ctrl_t ctrl); static void kbxd_deinit_default_ctrl (ctrl_t ctrl); static void handle_connections (gnupg_fd_t listen_fd); -static void check_own_socket (void); static int check_for_running_kbxd (int silent); - -/* Pth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - +#if CHECK_OWN_SOCKET_INTERVAL > 0 +static void *check_own_socket_thread (void *arg); +#endif +static void *check_others_thread (void *arg); /* * Functions. @@ -440,6 +441,7 @@ thread_init_once (void) * initialized and thus Libgcrypt could not set its system call * clamp. */ gcry_control (GCRYCTL_REINIT_SYSCALL_CLAMP, 0, 0); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); } @@ -447,7 +449,6 @@ static void initialize_modules (void) { thread_init_once (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); } @@ -497,7 +498,6 @@ main (int argc, char **argv ) assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); assuan_sock_init (); - assuan_sock_set_system_hooks (ASSUAN_SYSTEM_NPTH); setup_libassuan_logging (&opt.debug, kbxd_assuan_log_monitor); setup_libgcrypt_logging (); @@ -809,11 +809,6 @@ main (int argc, char **argv ) log_get_prefix (&oldflags); log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED); opt.running_detached = 1; - - /* Because we don't support running a program on the command - * line we can assume that the inotify things works and thus - * we can avoid the regular stat calls. */ - reliable_homedir_inotify = 1; } { @@ -1070,6 +1065,44 @@ get_kbxd_active_connection_count (void) } +/* Under W32, this function returns the handle of the scdaemon + notification event. Calling it the first time creates that + event. */ +#if defined(HAVE_W32_SYSTEM) +static void * +create_an_event (void) +{ + HANDLE h, h2; + SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE}; + + /* We need to use a manual reset event object due to the way our + w32-pth wait function works: If we would use an automatic + reset event we are not able to figure out which handle has + been signaled because at the time we single out the signaled + handles using WFSO the event has already been reset due to + the WFMO. */ + h = CreateEvent (&sa, TRUE, FALSE, NULL); + if (!h) + log_error ("can't create an event: %s\n", w32_strerror (-1) ); + else if (!DuplicateHandle (GetCurrentProcess(), h, + GetCurrentProcess(), &h2, + EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0)) + { + log_error ("setting synchronize for an event failed: %s\n", + w32_strerror (-1) ); + CloseHandle (h); + } + else + { + CloseHandle (h); + return h2; + } + + return INVALID_HANDLE_VALUE; +} +#endif /*HAVE_W32_SYSTEM*/ + + /* Create a name for the socket in the home directory as using * STANDARD_NAME. We also check for valid characters as well as * against a maximum allowed length for a Unix domain socket is done. @@ -1282,38 +1315,6 @@ create_directories (void) -/* This is the worker for the ticker. It is called every few seconds - * and may only do fast operations. */ -static void -handle_tick (void) -{ - static time_t last_minute; - struct stat statbuf; - - if (!last_minute) - last_minute = time (NULL); - - /* Code to be run from time to time. */ -#if CHECK_OWN_SOCKET_INTERVAL > 0 - if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL)) - { - check_own_socket (); - last_minute = time (NULL); - } -#endif - - - /* Check whether the homedir is still available. */ - if (!shutdown_pending - && (!have_homedir_inotify || !reliable_homedir_inotify) - && gnupg_stat (gnupg_homedir (), &statbuf) && errno == ENOENT) - { - shutdown_pending = 1; - log_info ("homedir has been removed - shutting down\n"); - } -} - - /* A global function which allows us to call the reload stuff from * other places too. This is only used when build for W32. */ void @@ -1359,6 +1360,11 @@ handle_signal (int signo) kbxd_sigusr2_action (); break; + case SIGCONT: + /* Do nothing, but break the syscall. */ + log_debug ("SIGCONT received - breaking select\n"); + break; + case SIGTERM: if (!shutdown_pending) log_info ("SIGTERM received - shutting down ...\n"); @@ -1396,7 +1402,7 @@ check_nonce (ctrl_t ctrl, assuan_sock_nonce_t *nonce) if (assuan_sock_check_nonce (ctrl->thread_startup.fd, nonce)) { log_info (_("error reading nonce on fd %d: %s\n"), - FD2INT(ctrl->thread_startup.fd), strerror (errno)); + FD_DBG (ctrl->thread_startup.fd), strerror (errno)); assuan_sock_close (ctrl->thread_startup.fd); xfree (ctrl); return -1; @@ -1416,7 +1422,7 @@ do_start_connection_thread (ctrl_t ctrl) kbxd_init_default_ctrl (ctrl); if (opt.verbose && !DBG_IPC) log_info (_("handler 0x%lx for fd %d started\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); session_id = ++last_session_id; if (!session_id) @@ -1424,7 +1430,7 @@ do_start_connection_thread (ctrl_t ctrl) kbxd_start_command_handler (ctrl, ctrl->thread_startup.fd, session_id); if (opt.verbose && !DBG_IPC) log_info (_("handler 0x%lx for fd %d terminated\n"), - (unsigned long) npth_self(), FD2INT(ctrl->thread_startup.fd)); + (unsigned long) npth_self(), FD_DBG (ctrl->thread_startup.fd)); kbxd_deinit_default_ctrl (ctrl); xfree (ctrl); @@ -1450,6 +1456,28 @@ start_connection_thread (void *arg) } +static void +keyboxd_kick_the_loop (void) +{ + /* Kick the select loop. */ +#ifdef HAVE_W32_SYSTEM + int ret = SetEvent (the_event2); + if (ret == 0) + log_error ("SetEvent for agent_kick_the_loop failed: %s\n", + w32_strerror (-1)); +#else +# ifdef HAVE_PSELECT_NO_EINTR + write (event_pipe_fd, "", 1); +# else + int ret = kill (main_thread_pid, SIGCONT); + if (ret < 0) + log_error ("sending signal for agent_kick_the_loop failed: %s\n", + gpg_strerror (gpg_error_from_syserror ())); +# endif +#endif +} + + /* Connection handler loop. Wait for connection requests and spawn a * thread after accepting a connection. */ static void @@ -1464,12 +1492,14 @@ handle_connections (gnupg_fd_t listen_fd) gnupg_fd_t fd; int nfd; int saved_errno; - struct timespec abstime; - struct timespec curtime; - struct timespec timeout; #ifdef HAVE_W32_SYSTEM HANDLE events[2]; unsigned int events_set; +#else + int signo; +# ifdef HAVE_PSELECT_NO_EINTR + int pipe_fd[2]; +# endif #endif int sock_inotify_fd = -1; int home_inotify_fd = -1; @@ -1480,7 +1510,7 @@ handle_connections (gnupg_fd_t listen_fd) } listentbl[] = { { "std", start_connection_thread }, }; - + int have_homedir_inotify = 0; ret = npth_attr_init(&tattr); if (ret) @@ -1493,10 +1523,23 @@ handle_connections (gnupg_fd_t listen_fd) npth_sigev_add (SIGUSR1); npth_sigev_add (SIGUSR2); npth_sigev_add (SIGINT); + npth_sigev_add (SIGCONT); npth_sigev_add (SIGTERM); npth_sigev_fini (); +# ifdef HAVE_PSELECT_NO_EINTR + ret = gnupg_create_pipe (pipe_fd); + if (ret) + { + log_error ("pipe creation failed: %s\n", gpg_strerror (ret)); + return; + } + event_pipe_fd = pipe_fd[1]; +# else + main_thread_pid = getpid (); +# endif #else - events[0] = INVALID_HANDLE_VALUE; + events[0] = the_event2 = create_an_event (); + events[1] = INVALID_HANDLE_VALUE; #endif if (disable_check_own_socket) @@ -1508,10 +1551,8 @@ handle_connections (gnupg_fd_t listen_fd) gpg_strerror (err)); } - if (disable_check_own_socket) - home_inotify_fd = -1; - else if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd, - gnupg_homedir ()))) + if ((err = gnupg_inotify_watch_delete_self (&home_inotify_fd, + gnupg_homedir ()))) { if (gpg_err_code (err) != GPG_ERR_NOT_SUPPORTED) log_info ("error enabling daemon termination by homedir removal: %s\n", @@ -1520,9 +1561,29 @@ handle_connections (gnupg_fd_t listen_fd) else have_homedir_inotify = 1; +#if CHECK_OWN_SOCKET_INTERVAL > 0 + if (!disable_check_own_socket && sock_inotify_fd == -1) + { + npth_t thread; + + err = npth_create (&thread, &tattr, check_own_socket_thread, NULL); + if (err) + log_error ("error spawning check_own_socket_thread: %s\n", strerror (err)); + } +#endif + + if (!have_homedir_inotify) + { + npth_t thread; + + err = npth_create (&thread, &tattr, check_others_thread, NULL); + if (err) + log_error ("error spawning check_others_thread: %s\n", strerror (err)); + } + FD_ZERO (&fdset); FD_SET (FD2INT (listen_fd), &fdset); - nfd = FD2INT (listen_fd); + nfd = FD2NUM (listen_fd); if (sock_inotify_fd != -1) { FD_SET (sock_inotify_fd, &fdset); @@ -1538,9 +1599,6 @@ handle_connections (gnupg_fd_t listen_fd) listentbl[0].l_fd = listen_fd; - npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; - for (;;) { /* Shutdown test. */ @@ -1573,28 +1631,21 @@ handle_connections (gnupg_fd_t listen_fd) read_fdset = fdset; - npth_clock_gettime (&curtime); - if (!(npth_timercmp (&curtime, &abstime, <))) - { - /* Timeout. */ - handle_tick (); - npth_clock_gettime (&abstime); - abstime.tv_sec += TIMERTICK_INTERVAL; - } - npth_timersub (&abstime, &curtime, &timeout); +#ifdef HAVE_PSELECT_NO_EINTR + FD_SET (pipe_fd[0], &read_fdset); + if (nfd < pipe_fd[0]) + nfd = pipe_fd[0]; +#endif #ifndef HAVE_W32_SYSTEM - ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, NULL, npth_sigev_sigmask ()); saved_errno = errno; - { - int signo; - while (npth_sigev_get_pending (&signo)) - handle_signal (signo); - } + while (npth_sigev_get_pending (&signo)) + handle_signal (signo); #else - ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, + ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, NULL, events, &events_set); saved_errno = errno; @@ -1610,6 +1661,22 @@ handle_connections (gnupg_fd_t listen_fd) gnupg_sleep (1); continue; } + + if ((problem_detected & KEYBOXD_PROBLEM_SOCKET_TAKEOVER)) + { + /* We may not remove the socket as it is now in use by another + server. */ + inhibit_socket_removal = 1; + shutdown_pending = 2; + log_info ("this process is useless - shutting down\n"); + } + + if ((problem_detected & KEYBOXD_PROBLEM_HOMEDIR_REMOVED)) + { + shutdown_pending = 1; + log_info ("homedir has been removed - shutting down\n"); + } + if (ret <= 0) { /* Interrupt or timeout. Will be handled when calculating the @@ -1617,6 +1684,15 @@ handle_connections (gnupg_fd_t listen_fd) continue; } +#ifdef HAVE_PSELECT_NO_EINTR + if (FD_ISSET (pipe_fd[0], &read_fdset)) + { + char buf[256]; + + read (pipe_fd[0], buf, sizeof buf); + } +#endif + /* The inotify fds are set even when a shutdown is pending (see * above). So we must handle them in any case. To avoid that * they trigger a second time we close them immediately. */ @@ -1653,8 +1729,8 @@ handle_connections (gnupg_fd_t listen_fd) continue; plen = sizeof paddr; - fd = INT2FD (npth_accept (FD2INT(listentbl[idx].l_fd), - (struct sockaddr *)&paddr, &plen)); + fd = assuan_sock_accept (listentbl[idx].l_fd, + (struct sockaddr *)&paddr, &plen); if (fd == GNUPG_INVALID_FD) { log_error ("accept failed for %s: %s\n", @@ -1687,13 +1763,21 @@ handle_connections (gnupg_fd_t listen_fd) close (sock_inotify_fd); if (home_inotify_fd != -1) close (home_inotify_fd); +#ifdef HAVE_W32_SYSTEM + if (the_event2 != INVALID_HANDLE_VALUE) + CloseHandle (the_event2); +#endif +#ifdef HAVE_PSELECT_NO_EINTR + close (pipe_fd[0]); + close (pipe_fd[1]); +#endif cleanup (); log_info (_("%s %s stopped\n"), gpgrt_strusage(11), gpgrt_strusage(13)); npth_attr_destroy (&tattr); } - +#if CHECK_OWN_SOCKET_INTERVAL > 0 /* Helper for check_own_socket. */ static gpg_error_t check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length) @@ -1704,20 +1788,18 @@ check_own_socket_pid_cb (void *opaque, const void *buffer, size_t length) } -/* The thread running the actual check. We need to run this in a - * separate thread so that check_own_thread can be called from the - * timer tick. */ -static void * -check_own_socket_thread (void *arg) +/* Check whether we are still listening on our own socket. In case + another gpg-agent process started after us has taken ownership of + our socket, we would linger around without any real task. Thus we + better check once in a while whether we are really needed. */ +static int +do_check_own_socket (const char *sockname) { int rc; - char *sockname = arg; assuan_context_t ctx = NULL; membuf_t mb; char *buffer; - check_own_socket_running++; - rc = assuan_new (&ctx); if (rc) { @@ -1755,57 +1837,70 @@ check_own_socket_thread (void *arg) xfree (buffer); leave: - xfree (sockname); if (ctx) assuan_release (ctx); - if (rc) - { - /* We may not remove the socket as it is now in use by another - * server. */ - inhibit_socket_removal = 1; - shutdown_pending = 2; - log_info ("this process is useless - shutting down\n"); - } - check_own_socket_running--; - return NULL; -} + return rc; +} -/* Check whether we are still listening on our own socket. In case - * another keyboxd process started after us has taken ownership of our - * socket, we would linger around without any real task. Thus we - * better check once in a while whether we are really needed. */ -static void -check_own_socket (void) +/* The thread running the actual check. */ +static void * +check_own_socket_thread (void *arg) { char *sockname; - npth_t thread; - npth_attr_t tattr; - int err; - if (disable_check_own_socket) - return; - - if (check_own_socket_running || shutdown_pending) - return; /* Still running or already shutting down. */ + (void)arg; sockname = make_filename_try (gnupg_socketdir (), KEYBOXD_SOCK_NAME, NULL); if (!sockname) - return; /* Out of memory. */ + return NULL; /* Out of memory. */ - err = npth_attr_init (&tattr); - if (err) + while (!problem_detected) { - xfree (sockname); - return; + if (shutdown_pending) + goto leave; + + gnupg_sleep (CHECK_OWN_SOCKET_INTERVAL); + + if (do_check_own_socket (sockname)) + problem_detected |= KEYBOXD_PROBLEM_SOCKET_TAKEOVER; } - npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); - err = npth_create (&thread, &tattr, check_own_socket_thread, sockname); - if (err) - log_error ("error spawning check_own_socket_thread: %s\n", strerror (err)); - npth_attr_destroy (&tattr); + + keyboxd_kick_the_loop (); + + leave: + xfree (sockname); + return NULL; } +#endif + +/* The thread running other checks. */ +static void * +check_others_thread (void *arg) +{ + const char *homedir = gnupg_homedir (); + (void)arg; + + while (!problem_detected) + { + struct stat statbuf; + + if (shutdown_pending) + goto leave; + + gnupg_sleep (CHECK_PROBLEMS_INTERVAL); + + /* Check whether the homedir is still available. */ + if (gnupg_stat (homedir, &statbuf) && errno == ENOENT) + problem_detected |= KEYBOXD_PROBLEM_HOMEDIR_REMOVED; + } + + keyboxd_kick_the_loop (); + + leave: + return NULL; +} /* Figure out whether a keyboxd is available and running. Prints an @@ -63,7 +63,7 @@ msgid "" msgstr "" "Project-Id-Version: gnupg\n" "Report-Msgid-Bugs-To: [email protected]\n" -"PO-Revision-Date: 2023-11-29 13:11+0000\n" +"PO-Revision-Date: 2024-04-19 16:10+0100\n" "Last-Translator: Daniel Cerqueira <[email protected]>\n" "Language-Team: pt <[email protected]>\n" "Language: pt\n" @@ -881,28 +881,8 @@ msgid "waiting for process %d to terminate failed: %s\n" msgstr "falha ao aguardar pelo encerramento do processo %d: %s\n" #, c-format -msgid "error running '%s': probably not installed\n" -msgstr "" - -#, fuzzy, c-format -#| msgid "error accessing '%s': http status %u\n" -msgid "error running '%s': exit status %d\n" -msgstr "erro ao aceder '%s': status http %u\n" - -#, fuzzy, c-format -#| msgid "error opening '%s': %s\n" -msgid "error running '%s': terminated\n" -msgstr "erro ao abrir '%s': %s\n" - -#, fuzzy, c-format -#| msgid "waiting for process %d to terminate failed: %s\n" -msgid "waiting for processes to terminate failed: %s\n" -msgstr "falha ao aguardar pelo encerramento do processo %d: %s\n" - -#, fuzzy, c-format -#| msgid "error getting list of cards: %s\n" -msgid "error getting exit code of process %d: %s\n" -msgstr "erro ao obter a lista de cartões: %s\n" +msgid "waiting for process to terminate failed: ec=%d\n" +msgstr "falha ao esperar que o processo terminasse: ec=%d\n" #, c-format msgid "can't connect to '%s': %s\n" @@ -2157,6 +2137,9 @@ msgstr "criar saÃda blindada ASCII" msgid "|FILE|write output to FILE" msgstr "|FILE|escrever saÃda em FILE" +msgid "use canonical text mode" +msgstr "usar modo de texto canónico" + msgid "|N|set compress level to N (0 disables)" msgstr "|N|definir nÃvel de compressão para N (0 desabilita)" @@ -4758,7 +4741,7 @@ msgstr "chave pública é %s\n" #, c-format msgid "encrypted with %s key, ID %s, created %s\n" -msgstr "cifrado com chave %s, ID %s, criado em %s\n" +msgstr "cifrado com chave %s, ID %s, criada em %s\n" #, c-format msgid " \"%s\"\n" @@ -4924,7 +4907,7 @@ msgid "textmode" msgstr "modo de texto" msgid "unknown" -msgstr "desconhecido" +msgstr "desconhec." msgid ", key algorithm " msgstr ", algoritmo da chave " @@ -6723,9 +6706,7 @@ msgstr "o acesso aos comandos admin não está configurado\n" msgid "||Please enter the PIN" msgstr "||Introduza o PIN" -#, fuzzy -#| msgid "||Please enter the Reset Code for the card" -msgid "|R|Please enter the Reset Code for the card" +msgid "||Please enter the Reset Code for the card" msgstr "||Introduza o Código de Reset do cartão" #, c-format @@ -8987,18 +8968,8 @@ msgstr "armazenar um certificado em um objeto de dados" msgid "store a private key to a data object" msgstr "armazenar uma chave privada em um objeto de dados" -msgid "run various checks on the keys" -msgstr "" - msgid "Yubikey management commands" msgstr "comandos de gerir uma Yubikey" msgid "manage the command history" msgstr "gerir o histórico de comandos" - -#~ msgid "use canonical text mode" -#~ msgstr "usar modo de texto canónico" - -#, c-format -#~ msgid "waiting for process to terminate failed: ec=%d\n" -#~ msgstr "falha ao esperar que o processo terminasse: ec=%d\n" diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index fe0855e77..c2a8d5ffe 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -119,8 +119,8 @@ static struct { { 0x00D6, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Signature"}, { 0x00D7, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Decryption"}, { 0x00D8, 0, 0x6E, 1, 0, 0, 0, 0, "UIF for Authentication"}, - { 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"}, - { 0x00FA, 0, 0, 1, 0, 0, 0, 2, "Algorithm Information"}, + { 0x00F9, 1, 0, 1, 0, 0, 0, 0, "KDF data object"}, + { 0x00FA, 1, 0, 1, 0, 0, 0, 2, "Algorithm Information"}, { 0 } }; @@ -310,6 +310,7 @@ get_manufacturer (unsigned int no) case 0x000D: return "Dangerous Things"; case 0x000E: return "Excelsecu"; case 0x000F: return "Nitrokey"; + case 0x0010: return "NeoPGP"; case 0x002A: return "Magrathea"; case 0x0042: return "GnuPG e.V."; diff --git a/scd/app-piv.c b/scd/app-piv.c index dc92bd2e2..d7f9acca3 100644 --- a/scd/app-piv.c +++ b/scd/app-piv.c @@ -128,45 +128,45 @@ static struct data_object_s data_objects[] = { "Discovery Object" }, { 0x5FC10C, 0, 0,1, 0,0, 0, "", "2.96.96", NULL, "Key History Object" }, - { 0x5FC10D, 0, 0,1, 0,0, 0, "82", "2.16.1", "e", + { 0x5FC10D, 0, 0,1, 0,0, 1, "82", "2.16.1", "e", "Retired Cert Key Mgm 1" }, - { 0x5FC10E, 0, 0,1, 0,0, 0, "83", "2.16.2", "e", + { 0x5FC10E, 0, 0,1, 0,0, 1, "83", "2.16.2", "e", "Retired Cert Key Mgm 2" }, - { 0x5FC10F, 0, 0,1, 0,0, 0, "84", "2.16.3", "e", + { 0x5FC10F, 0, 0,1, 0,0, 1, "84", "2.16.3", "e", "Retired Cert Key Mgm 3" }, - { 0x5FC110, 0, 0,1, 0,0, 0, "85", "2.16.4", "e", + { 0x5FC110, 0, 0,1, 0,0, 1, "85", "2.16.4", "e", "Retired Cert Key Mgm 4" }, - { 0x5FC111, 0, 0,1, 0,0, 0, "86", "2.16.5", "e", + { 0x5FC111, 0, 0,1, 0,0, 1, "86", "2.16.5", "e", "Retired Cert Key Mgm 5" }, - { 0x5FC112, 0, 0,1, 0,0, 0, "87", "2.16.6", "e", + { 0x5FC112, 0, 0,1, 0,0, 1, "87", "2.16.6", "e", "Retired Cert Key Mgm 6" }, - { 0x5FC113, 0, 0,1, 0,0, 0, "88", "2.16.7", "e", + { 0x5FC113, 0, 0,1, 0,0, 1, "88", "2.16.7", "e", "Retired Cert Key Mgm 7" }, - { 0x5FC114, 0, 0,1, 0,0, 0, "89", "2.16.8", "e", + { 0x5FC114, 0, 0,1, 0,0, 1, "89", "2.16.8", "e", "Retired Cert Key Mgm 8" }, - { 0x5FC115, 0, 0,1, 0,0, 0, "8A", "2.16.9", "e", + { 0x5FC115, 0, 0,1, 0,0, 1, "8A", "2.16.9", "e", "Retired Cert Key Mgm 9" }, - { 0x5FC116, 0, 0,1, 0,0, 0, "8B", "2.16.10", "e", + { 0x5FC116, 0, 0,1, 0,0, 1, "8B", "2.16.10", "e", "Retired Cert Key Mgm 10" }, - { 0x5FC117, 0, 0,1, 0,0, 0, "8C", "2.16.11", "e", + { 0x5FC117, 0, 0,1, 0,0, 1, "8C", "2.16.11", "e", "Retired Cert Key Mgm 11" }, - { 0x5FC118, 0, 0,1, 0,0, 0, "8D", "2.16.12", "e", + { 0x5FC118, 0, 0,1, 0,0, 1, "8D", "2.16.12", "e", "Retired Cert Key Mgm 12" }, - { 0x5FC119, 0, 0,1, 0,0, 0, "8E", "2.16.13", "e", + { 0x5FC119, 0, 0,1, 0,0, 1, "8E", "2.16.13", "e", "Retired Cert Key Mgm 13" }, - { 0x5FC11A, 0, 0,1, 0,0, 0, "8F", "2.16.14", "e", + { 0x5FC11A, 0, 0,1, 0,0, 1, "8F", "2.16.14", "e", "Retired Cert Key Mgm 14" }, - { 0x5FC11B, 0, 0,1, 0,0, 0, "90", "2.16.15", "e", + { 0x5FC11B, 0, 0,1, 0,0, 1, "90", "2.16.15", "e", "Retired Cert Key Mgm 15" }, - { 0x5FC11C, 0, 0,1, 0,0, 0, "91", "2.16.16", "e", + { 0x5FC11C, 0, 0,1, 0,0, 1, "91", "2.16.16", "e", "Retired Cert Key Mgm 16" }, - { 0x5FC11D, 0, 0,1, 0,0, 0, "92", "2.16.17", "e", + { 0x5FC11D, 0, 0,1, 0,0, 1, "92", "2.16.17", "e", "Retired Cert Key Mgm 17" }, - { 0x5FC11E, 0, 0,1, 0,0, 0, "93", "2.16.18", "e", + { 0x5FC11E, 0, 0,1, 0,0, 1, "93", "2.16.18", "e", "Retired Cert Key Mgm 18" }, - { 0x5FC11F, 0, 0,1, 0,0, 0, "94", "2.16.19", "e", + { 0x5FC11F, 0, 0,1, 0,0, 1, "94", "2.16.19", "e", "Retired Cert Key Mgm 19" }, - { 0x5FC120, 0, 0,1, 0,0, 0, "95", "2.16.20", "e", + { 0x5FC120, 0, 0,1, 0,0, 1, "95", "2.16.20", "e", "Retired Cert Key Mgm 20" }, { 0x5FC121, 0, 2,2, 0,0, 0, "", "2.16.21", NULL, "Cardholder Iris Images" }, @@ -3543,7 +3543,7 @@ do_with_keygrip (app_t app, ctrl_t ctrl, int action, } if (capability == GCRY_PK_USAGE_ENCR) { - if (strcmp (data_objects[i].keyref, "9D")) + if (strcmp (data_objects[i].usage, "e")) continue; } if (capability == GCRY_PK_USAGE_AUTH) @@ -2385,6 +2385,18 @@ app_check_pin (card_t card, ctrl_t ctrl, const char *keyidstr, static void +setup_env (struct spawn_cb_arg *sca) +{ +#ifdef HAVE_W32_SYSTEM + (void)sca; /* Not supported on Windows. */ +#else + char *v = sca->arg; + + putenv (v); +#endif +} + +static void report_change (int slot, int old_status, int cur_status) { char *homestr, *envstr; @@ -2411,12 +2423,9 @@ report_change (int slot, int old_status, int cur_status) else { gpg_error_t err; - const char *args[9], *envs[2]; + const char *args[9]; char numbuf1[30], numbuf2[30], numbuf3[30]; - envs[0] = envstr; - envs[1] = NULL; - sprintf (numbuf1, "%d", slot); sprintf (numbuf2, "0x%04X", old_status); sprintf (numbuf3, "0x%04X", cur_status); @@ -2433,7 +2442,9 @@ report_change (int slot, int old_status, int cur_status) args[8] = NULL; fname = make_filename (gnupg_homedir (), "scd-event", NULL); - err = gnupg_spawn_process_detached (fname, args, envs); + err = gnupg_process_spawn (fname, args, + GNUPG_PROCESS_DETACHED, + setup_env, envstr, NULL); if (err && gpg_err_code (err) != GPG_ERR_ENOENT) log_error ("failed to run event handler '%s': %s\n", fname, gpg_strerror (err)); diff --git a/scd/command.c b/scd/command.c index 0cf66d08c..a2274f15a 100644 --- a/scd/command.c +++ b/scd/command.c @@ -957,7 +957,8 @@ pin_cb (void *opaque, const char *info, char **retstr) We ignore any value returned. */ if (info) { - log_debug ("prompting for pinpad entry '%s'\n", info); + if (DBG_IPC) + log_debug ("prompting for pinpad entry '%s'\n", info); rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info); if (rc < 0) return gpg_error (gpg_err_code_from_errno (errno)); @@ -966,7 +967,8 @@ pin_cb (void *opaque, const char *info, char **retstr) } else { - log_debug ("dismiss pinpad entry prompt\n"); + if (DBG_IPC) + log_debug ("dismiss pinpad entry prompt\n"); rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT", &value, &valuelen, MAXLEN_PIN); } @@ -976,7 +978,8 @@ pin_cb (void *opaque, const char *info, char **retstr) } *retstr = NULL; - log_debug ("asking for PIN '%s'\n", info); + if (DBG_IPC) + log_debug ("asking for PIN '%s'\n", info); rc = gpgrt_asprintf (&command, "NEEDPIN %s", info); if (rc < 0) @@ -2942,10 +2945,10 @@ void send_client_notifications (card_t card, int removal) { struct { - pid_t pid; #ifdef HAVE_W32_SYSTEM HANDLE handle; #else + pid_t pid; int signo; #endif } killed[50]; @@ -2965,10 +2968,10 @@ send_client_notifications (card_t card, int removal) if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card) { - pid_t pid; #ifdef HAVE_W32_SYSTEM HANDLE handle; #else + pid_t pid; int signo; #endif @@ -2983,32 +2986,33 @@ send_client_notifications (card_t card, int removal) if (!sl->event_signal || !sl->assuan_ctx) continue; - pid = assuan_get_pid (sl->assuan_ctx); - #ifdef HAVE_W32_SYSTEM handle = sl->event_signal; for (kidx=0; kidx < killidx; kidx++) - if (killed[kidx].pid == pid - && killed[kidx].handle == handle) + if (killed[kidx].handle == handle) break; if (kidx < killidx) - log_info ("event %p (%p) already triggered for client %d\n", - sl->event_signal, handle, (int)pid); + { + if (opt.verbose) + log_info ("event %p already triggered for client\n", + sl->event_signal); + } else { - log_info ("triggering event %p (%p) for client %d\n", - sl->event_signal, handle, (int)pid); + if (opt.verbose) + log_info ("triggering event %p for client\n", + sl->event_signal); if (!SetEvent (handle)) log_error ("SetEvent(%p) failed: %s\n", sl->event_signal, w32_strerror (-1)); if (killidx < DIM (killed)) { - killed[killidx].pid = pid; killed[killidx].handle = handle; killidx++; } } #else /*!HAVE_W32_SYSTEM*/ + pid = assuan_get_pid (sl->assuan_ctx); signo = sl->event_signal; if (pid != (pid_t)(-1) && pid && signo > 0) @@ -3018,12 +3022,16 @@ send_client_notifications (card_t card, int removal) && killed[kidx].signo == signo) break; if (kidx < killidx) - log_info ("signal %d already sent to client %d\n", - signo, (int)pid); + { + if (opt.verbose) + log_info ("signal %d already sent to client %d\n", + signo, (int)pid); + } else { - log_info ("sending signal %d to client %d\n", - signo, (int)pid); + if (opt.verbose) + log_info ("sending signal %d to client %d\n", + signo, (int)pid); kill (pid, signo); if (killidx < DIM (killed)) { diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 1a8705b18..2a9b0923c 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -284,9 +284,6 @@ static gnupg_fd_t create_server_socket (const char *name, static void *start_connection_thread (void *arg); static void handle_connections (gnupg_fd_t listen_fd); -/* Pth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - static int active_connections; @@ -492,7 +489,6 @@ main (int argc, char **argv ) malloc_hooks.free = gcry_free; assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); assuan_sock_init (); setup_libassuan_logging (&opt.debug, NULL); @@ -782,6 +778,7 @@ main (int argc, char **argv ) npth_init (); setup_signal_mask (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* If --debug-allow-core-dump has been given we also need to switch the working directory to a place where we can actually @@ -923,6 +920,7 @@ main (int argc, char **argv ) npth_init (); setup_signal_mask (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* Detach from tty and put process into a new session. */ if (!nodetach ) @@ -1207,7 +1205,7 @@ start_connection_thread (void *arg) && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_nonce)) { log_info (_("error reading nonce on fd %d: %s\n"), - FD2INT(ctrl->thread_startup.fd), strerror (errno)); + FD_DBG (ctrl->thread_startup.fd), strerror (errno)); assuan_sock_close (ctrl->thread_startup.fd); xfree (ctrl); return NULL; @@ -1218,7 +1216,7 @@ start_connection_thread (void *arg) scd_init_default_ctrl (ctrl); if (opt.verbose) log_info (_("handler for fd %d started\n"), - FD2INT(ctrl->thread_startup.fd)); + FD_DBG (ctrl->thread_startup.fd)); /* If this is a pipe server, we request a shutdown if the command handler asked for it. With the next ticker event and given that @@ -1230,7 +1228,7 @@ start_connection_thread (void *arg) if (opt.verbose) log_info (_("handler for fd %d terminated\n"), - FD2INT (ctrl->thread_startup.fd)); + FD_DBG (ctrl->thread_startup.fd)); scd_deinit_default_ctrl (ctrl); xfree (ctrl); @@ -1337,7 +1335,7 @@ handle_connections (gnupg_fd_t listen_fd) if (listen_fd != GNUPG_INVALID_FD) { FD_SET (FD2INT (listen_fd), &fdset); - nfd = FD2INT (listen_fd); + nfd = FD2NUM (listen_fd); } for (;;) @@ -1423,8 +1421,8 @@ handle_connections (gnupg_fd_t listen_fd) gnupg_fd_t fd; plen = sizeof paddr; - fd = INT2FD (npth_accept (FD2INT (listen_fd), - (struct sockaddr *)&paddr, &plen)); + fd = assuan_sock_accept (listen_fd, + (struct sockaddr *)&paddr, &plen); if (fd == GNUPG_INVALID_FD) { log_error ("accept failed: %s\n", strerror (errno)); @@ -1441,7 +1439,7 @@ handle_connections (gnupg_fd_t listen_fd) npth_t thread; snprintf (threadname, sizeof threadname, "conn fd=%d", - FD2INT (fd)); + FD_DBG (fd)); ctrl->thread_startup.fd = fd; ret = npth_create (&thread, &tattr, start_connection_thread, ctrl); if (ret) diff --git a/sm/call-agent.c b/sm/call-agent.c index eb6671692..acce19058 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -118,7 +118,8 @@ start_agent (ctrl_t ctrl) opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 7fe7a68f5..22580bd12 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -228,7 +228,8 @@ start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r) err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c index 6ea481529..267c4f4b5 100644 --- a/sm/certreqgen-ui.c +++ b/sm/certreqgen-ui.c @@ -388,6 +388,11 @@ gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream) tty_printf (_(" (optional; end with an empty line):\n")); ask_mb_lines (&mb_uri, "Name-URI: "); + /* Extensions */ + tty_printf (_("Enter extensions")); + tty_printf (_(" (optional; end with an empty line):\n")); + ask_mb_lines (&mb_uri, "Extension: "); + /* Want a self-signed certificate? */ selfsigned = tty_get_answer_is_yes diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 75343385d..435333298 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -49,6 +49,11 @@ Signing-Key: 68A638998DFABAC510EA645CE34F9686B2EDF7EA %commit + Commnn extensions: + ExtKeyUsage: clientAuth (suggested) serverAuth (suggested) + -> 2.5.29.37 n 301406082B0601050507030206082B06010505070301 + + */ diff --git a/sm/decrypt.c b/sm/decrypt.c index 6d63189b8..5a947779f 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1052,7 +1052,7 @@ decrypt_gcm_filter (void *arg, /* Perform a decrypt operation. */ int -gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) +gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp) { int rc; gnupg_ksba_io_t b64reader = NULL; @@ -1063,7 +1063,6 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) ksba_stop_reason_t stopreason; KEYDB_HANDLE kh; int recp; - estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; char *curve = NULL; @@ -1079,14 +1078,6 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) goto leave; } - in_fp = es_fdopen_nc (in_fd, "rb"); - if (!in_fp) - { - rc = gpg_error_from_syserror (); - log_error ("fdopen() failed: %s\n", strerror (errno)); - goto leave; - } - rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) @@ -1520,7 +1511,6 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); - es_fclose (in_fp); if (dfparm.hd) gcry_cipher_close (dfparm.hd); return rc; diff --git a/sm/encrypt.c b/sm/encrypt.c index 741fe6206..202bbb92f 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -574,7 +574,8 @@ encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int -gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) +gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp, + estream_t out_fp) { gpg_error_t err = 0; gnupg_ksba_io_t b64writer = NULL; @@ -586,7 +587,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; - estream_t data_fp = NULL; certlist_t cl; int count; int compliant; @@ -622,15 +622,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) goto leave; } - /* Fixme: We should use the unlocked version of the es functions. */ - data_fp = es_fdopen_nc (data_fd, "rb"); - if (!data_fp) - { - err = gpg_error_from_syserror (); - log_error ("fdopen() failed: %s\n", gpg_strerror (err)); - goto leave; - } - err = ksba_reader_new (&reader); if (!err) err = ksba_reader_set_cb (reader, encrypt_cb, &encparm); @@ -855,7 +846,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) ksba_reader_release (reader); keydb_release (kh); xfree (dek); - es_fclose (data_fp); xfree (encparm.buffer); return err; } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 3948372e4..b1a5f09b5 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -117,6 +117,7 @@ enum cmd_and_opt_values { oLogTime, oEnableSpecialFilenames, + oDisableFdTranslation, oAgentProgram, oDisplay, @@ -428,6 +429,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")), ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), + ARGPARSE_s_n (oDisableFdTranslation, "disable-fd-translation", "@"), ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), @@ -537,7 +539,6 @@ static void set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void emergency_cleanup (void); -static int open_read (const char *filename); static estream_t open_es_fread (const char *filename, const char *mode); static estream_t open_es_fwrite (const char *filename); static void run_protect_tool (int argc, char **argv); @@ -599,10 +600,6 @@ our_md_test_algo (int algo) } -/* nPth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - - static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { @@ -1466,6 +1463,10 @@ main ( int argc, char **argv) enable_special_filenames (); break; + case oDisableFdTranslation: + disable_translate_sys2libc_fd (); + break; + case oValidationModel: parse_validation_model (pargs.r.ret_str); break; case oKeyServer: @@ -1611,8 +1612,8 @@ main ( int argc, char **argv) npth_init (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* if (opt.qualsig_approval && !opt.quiet) */ @@ -1801,7 +1802,7 @@ main ( int argc, char **argv) { log_info (_("importing common certificates '%s'\n"), filelist[0]); - gpgsm_import_files (&ctrl, 1, filelist, open_read); + gpgsm_import_files (&ctrl, 1, filelist, open_es_fread); } xfree (filelist[0]); } @@ -1939,9 +1940,20 @@ main ( int argc, char **argv) set_binary (stdin); if (!argc) /* Source is stdin. */ - err = gpgsm_encrypt (&ctrl, recplist, 0, fp); + err = gpgsm_encrypt (&ctrl, recplist, es_stdin, fp); else if (argc == 1) /* Source is the given file. */ - err = gpgsm_encrypt (&ctrl, recplist, open_read (*argv), fp); + { + estream_t data_fp = es_fopen (*argv, "rb"); + + if (!data_fp) + { + log_error (_("can't open '%s': %s\n"), *argv, + strerror (errno)); + gpgsm_exit (2); + } + err = gpgsm_encrypt (&ctrl, recplist, data_fp, fp); + es_fclose (data_fp); + } else wrong_args ("--encrypt [datafile]"); @@ -1960,10 +1972,20 @@ main ( int argc, char **argv) signing because that is what gpg does.*/ set_binary (stdin); if (!argc) /* Create from stdin. */ - err = gpgsm_sign (&ctrl, signerlist, 0, detached_sig, fp); + err = gpgsm_sign (&ctrl, signerlist, es_stdin, detached_sig, fp); else if (argc == 1) /* From file. */ - err = gpgsm_sign (&ctrl, signerlist, - open_read (*argv), detached_sig, fp); + { + estream_t data_fp = es_fopen (*argv, "rb"); + + if (!data_fp) + { + log_error (_("can't open '%s': %s\n"), *argv, + strerror (errno)); + gpgsm_exit (2); + } + err = gpgsm_sign (&ctrl, signerlist, data_fp, detached_sig, fp); + es_fclose (data_fp); + } else wrong_args ("--sign [datafile]"); @@ -2004,11 +2026,43 @@ main ( int argc, char **argv) fp = open_es_fwrite (opt.outfile); if (!argc) - gpgsm_verify (&ctrl, 0, -1, fp); /* normal signature from stdin */ + /* normal signature from stdin */ + gpgsm_verify (&ctrl, es_stdin, NULL, fp); else if (argc == 1) - gpgsm_verify (&ctrl, open_read (*argv), -1, fp); /* std signature */ + { + estream_t in_fp = es_fopen (*argv, "rb"); + + if (!in_fp) + { + log_error (_("can't open '%s': %s\n"), *argv, + strerror (errno)); + gpgsm_exit (2); + } + gpgsm_verify (&ctrl, in_fp, NULL, fp); /* std signature */ + es_fclose (in_fp); + } else if (argc == 2) /* detached signature (sig, detached) */ - gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL); + { + estream_t in_fp = es_fopen (*argv, "rb"); + estream_t data_fp = es_fopen (argv[1], "rb"); + + if (!in_fp) + { + log_error (_("can't open '%s': %s\n"), *argv, + strerror (errno)); + gpgsm_exit (2); + } + if (!data_fp) + { + log_error (_("can't open '%s': %s\n"), argv[1], + strerror (errno)); + gpgsm_exit (2); + } + + gpgsm_verify (&ctrl, in_fp, data_fp, NULL); + es_fclose (in_fp); + es_fclose (data_fp); + } else wrong_args ("--verify [signature [detached_data]]"); @@ -2022,9 +2076,19 @@ main ( int argc, char **argv) set_binary (stdin); if (!argc) - err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ + err = gpgsm_decrypt (&ctrl, es_stdin, fp); /* from stdin */ else if (argc == 1) - err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ + { + estream_t data_fp = es_fopen (*argv, "rb"); + if (!data_fp) + { + log_error (_("can't open '%s': %s\n"), *argv, + strerror (errno)); + gpgsm_exit (2); + } + err = gpgsm_decrypt (&ctrl, data_fp, fp); /* from file */ + es_fclose (data_fp); + } else wrong_args ("--decrypt [filename]"); @@ -2115,7 +2179,7 @@ main ( int argc, char **argv) case aImport: - gpgsm_import_files (&ctrl, argc, argv, open_read); + gpgsm_import_files (&ctrl, argc, argv, open_es_fread); break; case aExport: @@ -2316,49 +2380,24 @@ gpgsm_parse_validation_model (const char *model) } - -/* Open the FILENAME for read and return the file descriptor. Stop - with an error message in case of problems. "-" denotes stdin and - if special filenames are allowed the given fd is opened instead. */ -static int -open_read (const char *filename) -{ - int fd; - - if (filename[0] == '-' && !filename[1]) - { - set_binary (stdin); - return 0; /* stdin */ - } - fd = check_special_filename (filename, 0, 0); - if (fd != -1) - return fd; - fd = gnupg_open (filename, O_RDONLY | O_BINARY, 0); - if (fd == -1) - { - log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); - gpgsm_exit (2); - } - return fd; -} - /* Same as open_read but return an estream_t. */ static estream_t open_es_fread (const char *filename, const char *mode) { - int fd; + gnupg_fd_t fd; estream_t fp; if (filename[0] == '-' && !filename[1]) - fd = fileno (stdin); + return es_fpopen_nc (stdin, mode); else - fd = check_special_filename (filename, 0, 0); - if (fd != -1) + fd = gnupg_check_special_filename (filename); + if (fd != GNUPG_INVALID_FD) { - fp = es_fdopen_nc (fd, mode); + fp = open_stream_nc (fd, mode); if (!fp) { - log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); + log_error ("es_fdopen(%d) failed: %s\n", FD_DBG (fd), + strerror (errno)); gpgsm_exit (2); } return fp; @@ -2380,23 +2419,24 @@ open_es_fread (const char *filename, const char *mode) static estream_t open_es_fwrite (const char *filename) { - int fd; + gnupg_fd_t fd; estream_t fp; if (filename[0] == '-' && !filename[1]) { fflush (stdout); - fp = es_fdopen_nc (fileno(stdout), "wb"); + fp = es_fpopen_nc (stdout, "wb"); return fp; } - fd = check_special_filename (filename, 1, 0); - if (fd != -1) + fd = gnupg_check_special_filename (filename); + if (fd != GNUPG_INVALID_FD) { - fp = es_fdopen_nc (fd, "wb"); + fp = open_stream_nc (fd, "wb"); if (!fp) { - log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); + log_error ("es_fdopen(%d) failed: %s\n", + FD_DBG (fd), strerror (errno)); gpgsm_exit (2); } return fp; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index faa03a9f4..673ea1687 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -431,9 +431,9 @@ gpg_error_t gpgsm_show_certs (ctrl_t ctrl, int nfiles, char **files, estream_t fp); /*-- import.c --*/ -int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode); +int gpgsm_import (ctrl_t ctrl, estream_t in_fp, int reimport_mode); int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, - int (*of)(const char *fname)); + estream_t (*of)(const char *fname, const char *mode)); /*-- export.c --*/ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); @@ -444,23 +444,24 @@ void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int gpgsm_delete (ctrl_t ctrl, strlist_t names); /*-- verify.c --*/ -int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp); +int gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp, + estream_t out_fp); /*-- sign.c --*/ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert); int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, - int data_fd, int detached, estream_t out_fp); + estream_t data_fp, int detached, estream_t out_fp); /*-- encrypt.c --*/ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, - int in_fd, estream_t out_fp); + estream_t in_fp, estream_t out_fp); /*-- decrypt.c --*/ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen); -int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); +int gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp); /*-- certreqgen.c --*/ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream); diff --git a/sm/import.c b/sm/import.c index cbf8fa627..4f993ef30 100644 --- a/sm/import.c +++ b/sm/import.c @@ -37,6 +37,10 @@ #include "../common/membuf.h" #include "minip12.h" +#ifndef O_BINARY +#define O_BINARY 0 +#endif + /* The arbitrary limit of one PKCS#12 object. */ #define MAX_P12OBJ_SIZE 128 /*kb*/ @@ -269,25 +273,16 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats, static int -import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) +import_one (ctrl_t ctrl, struct stats_s *stats, estream_t fp) { int rc; gnupg_ksba_io_t b64reader = NULL; ksba_reader_t reader; ksba_cert_t cert = NULL; ksba_cms_t cms = NULL; - estream_t fp = NULL; ksba_content_type_t ct; int any = 0; - fp = es_fdopen_nc (in_fd, "rb"); - if (!fp) - { - rc = gpg_error_from_syserror (); - log_error ("fdopen() failed: %s\n", strerror (errno)); - goto leave; - } - rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) @@ -388,7 +383,6 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) ksba_cms_release (cms); ksba_cert_release (cert); gnupg_ksba_destroy_reader (b64reader); - es_fclose (fp); return rc; } @@ -398,10 +392,9 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) fingerprints t re-import. The actual re-import is done by clearing the ephemeral flag. */ static int -reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) +reimport_one (ctrl_t ctrl, struct stats_s *stats, estream_t fp) { gpg_error_t err = 0; - estream_t fp = NULL; char line[100]; /* Sufficient for a fingerprint. */ KEYDB_HANDLE kh; KEYDB_SEARCH_DESC desc; @@ -417,14 +410,6 @@ reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) } keydb_set_ephemeral (kh, 1); - fp = es_fdopen_nc (in_fd, "r"); - if (!fp) - { - err = gpg_error_from_syserror (); - log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err)); - goto leave; - } - while (es_fgets (line, DIM(line)-1, fp) ) { if (*line && line[strlen(line)-1] != '\n') @@ -500,30 +485,29 @@ reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) if (es_ferror (fp)) { err = gpg_error_from_syserror (); - log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err)); + log_error ("error reading fp %p: %s\n", fp, gpg_strerror (err)); goto leave; } leave: ksba_cert_release (cert); keydb_release (kh); - es_fclose (fp); return err; } int -gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) +gpgsm_import (ctrl_t ctrl, estream_t in_fp, int reimport_mode) { int rc; struct stats_s stats; memset (&stats, 0, sizeof stats); if (reimport_mode) - rc = reimport_one (ctrl, &stats, in_fd); + rc = reimport_one (ctrl, &stats, in_fp); else - rc = import_one (ctrl, &stats, in_fd); + rc = import_one (ctrl, &stats, in_fp); print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a @@ -536,7 +520,7 @@ gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, - int (*of)(const char *fname)) + estream_t (*of)(const char *fname, const char *mode)) { int rc = 0; struct stats_s stats; @@ -544,14 +528,19 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, memset (&stats, 0, sizeof stats); if (!nfiles) - rc = import_one (ctrl, &stats, 0); + { +#ifdef HAVE_DOSISH_SYSTEM + setmode (0, O_BINARY); +#endif + rc = import_one (ctrl, &stats, es_stdin); + } else { for (; nfiles && !rc ; nfiles--, files++) { - int fd = of (*files); - rc = import_one (ctrl, &stats, fd); - close (fd); + estream_t fp = of (*files, "rb"); + rc = import_one (ctrl, &stats, fp); + es_fclose (fp); if (rc == -1/* legacy*/ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; } diff --git a/sm/keydb.c b/sm/keydb.c index 411720513..151ae8103 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -161,10 +161,17 @@ gpgsm_keydb_deinit_session_data (ctrl_t ctrl) log_error ("oops: trying to cleanup an active keydb context\n"); else { - kbx_client_data_release (kbl->kcd); - kbl->kcd = NULL; assuan_release (kbl->ctx); kbl->ctx = NULL; + /* + * Since there may be pipe output FD sent to the server (so + * that it can receive data through the pipe), we should + * release the assuan connection before releasing KBL->KCD. + * This way, the data receiving thread can finish cleanly, + * and we can join the thread. + */ + kbx_client_data_release (kbl->kcd); + kbl->kcd = NULL; } xfree (kbl); } @@ -516,7 +523,8 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) { @@ -580,7 +588,7 @@ open_context (ctrl_t ctrl, keydb_local_t *r_kbl) return err; } - err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 1); + err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 0); if (err) { assuan_release (kbl->ctx); diff --git a/sm/server.c b/sm/server.c index 184ec9379..f00b70d38 100644 --- a/sm/server.c +++ b/sm/server.c @@ -43,7 +43,7 @@ static FILE *statusfp; /* Data used to assuciate an Assuan context with local server data */ struct server_local_s { assuan_context_t assuan_ctx; - int message_fd; + estream_t message_fp; int list_internal; int list_external; int list_to_output; /* Write keylistings to the output fd. */ @@ -130,12 +130,12 @@ data_line_cookie_close (void *cookie) static void -close_message_fd (ctrl_t ctrl) +close_message_fp (ctrl_t ctrl) { - if (ctrl->server_local->message_fd != -1) + if (ctrl->server_local->message_fp) { - close (ctrl->server_local->message_fd); - ctrl->server_local->message_fd = -1; + es_fclose (ctrl->server_local->message_fp); + ctrl->server_local->message_fp = NULL; } } @@ -332,7 +332,7 @@ reset_notify (assuan_context_t ctx, char *line) ctrl->server_local->recplist = NULL; ctrl->server_local->signerlist = NULL; ctrl->always_trust = 0; - close_message_fd (ctrl); + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return 0; @@ -463,20 +463,26 @@ cmd_encrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); certlist_t cl; - int inp_fd, out_fd; + gnupg_fd_t inp_fd; + gnupg_fd_t out_fd; + estream_t inp_fp; estream_t out_fp; int rc; (void)line; - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); - out_fp = es_fdopen_nc (out_fd, "w"); + inp_fp = open_stream_nc (inp_fd, "r"); + if (!inp_fp) + return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); + + out_fp = open_stream_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); @@ -495,14 +501,15 @@ cmd_encrypt (assuan_context_t ctx, char *line) if (!rc) rc = gpgsm_encrypt (assuan_get_pointer (ctx), ctrl->server_local->recplist, - inp_fd, out_fp); + inp_fp, out_fp); + es_fclose (inp_fp); es_fclose (out_fp); gpgsm_release_certlist (ctrl->server_local->recplist); ctrl->server_local->recplist = NULL; ctrl->always_trust = 0; - /* Close and reset the fd */ - close_message_fd (ctrl); + /* Close and reset the fp and the fds */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; @@ -521,30 +528,37 @@ static gpg_error_t cmd_decrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int inp_fd, out_fd; + gnupg_fd_t inp_fd; + gnupg_fd_t out_fd; + estream_t inp_fp; estream_t out_fp; int rc; (void)line; - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); - out_fp = es_fdopen_nc (out_fd, "w"); + inp_fp = open_stream_nc (inp_fd, "r"); + if (!inp_fp) + return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); + + out_fp = open_stream_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); rc = start_audit_session (ctrl); if (!rc) - rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); + rc = gpgsm_decrypt (ctrl, inp_fp, out_fp); + es_fclose (inp_fp); es_fclose (out_fp); /* Close and reset the fds. */ - close_message_fd (ctrl); + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -566,30 +580,36 @@ cmd_verify (assuan_context_t ctx, char *line) { int rc; ctrl_t ctrl = assuan_get_pointer (ctx); - int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); + gnupg_fd_t fd = assuan_get_input_fd (ctx); + gnupg_fd_t out_fd = assuan_get_output_fd (ctx); + estream_t fp = NULL; estream_t out_fp = NULL; (void)line; - if (fd == -1) + if (fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - if (out_fd != -1) + fp = open_stream_nc (fd, "r"); + if (!fp) + return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); + + if (out_fd != GNUPG_INVALID_FD) { - out_fp = es_fdopen_nc (out_fd, "w"); + out_fp = open_stream_nc (out_fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } rc = start_audit_session (ctrl); if (!rc) - rc = gpgsm_verify (assuan_get_pointer (ctx), fd, - ctrl->server_local->message_fd, out_fp); + rc = gpgsm_verify (assuan_get_pointer (ctx), fp, + ctrl->server_local->message_fp, out_fp); + es_fclose (fp); es_fclose (out_fp); - /* Close and reset the fd. */ - close_message_fd (ctrl); + /* Close and reset the fp and the fd. */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -607,32 +627,39 @@ static gpg_error_t cmd_sign (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int inp_fd, out_fd; + gnupg_fd_t inp_fd; + gnupg_fd_t out_fd; + estream_t inp_fp; estream_t out_fp; int detached; int rc; - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); detached = has_option (line, "--detached"); - out_fp = es_fdopen_nc (out_fd, "w"); + inp_fp = open_stream_nc (inp_fd, "r"); + if (!inp_fp) + return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); + + out_fp = open_stream_nc (out_fd, "w"); if (!out_fp) return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed"); rc = start_audit_session (ctrl); if (!rc) rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist, - inp_fd, detached, out_fp); + inp_fp, detached, out_fp); + es_fclose (inp_fp); es_fclose (out_fp); - /* close and reset the fd */ - close_message_fd (ctrl); + /* close and reset the fp and the fds */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -657,18 +684,24 @@ cmd_import (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int rc; - int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); + gnupg_fd_t fd = assuan_get_input_fd (ctx); int reimport = has_option (line, "--re-import"); + estream_t fp; (void)line; - if (fd == -1) + if (fd == GNUPG_INVALID_FD) + return set_error (GPG_ERR_ASS_NO_INPUT, NULL); + + fp = open_stream_nc (fd, "r"); + if (!fp) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport); + rc = gpgsm_import (assuan_get_pointer (ctx), fp, reimport); + es_fclose (fp); - /* close and reset the fd */ - close_message_fd (ctrl); + /* close and reset the fp and the fds */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -769,15 +802,15 @@ cmd_export (assuan_context_t ctx, char *line) } else { - int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); + gnupg_fd_t fd = assuan_get_output_fd (ctx); estream_t out_fp; - if (fd == -1) + if (fd == GNUPG_INVALID_FD) { free_strlist (list); return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); } - out_fp = es_fdopen_nc (fd, "w"); + out_fp = open_stream_nc (fd, "w"); if (!out_fp) { free_strlist (list); @@ -793,8 +826,8 @@ cmd_export (assuan_context_t ctx, char *line) } free_strlist (list); - /* Close and reset the fds. */ - close_message_fd (ctrl); + /* Close and reset the fp and the fds. */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return 0; @@ -842,8 +875,8 @@ cmd_delkeys (assuan_context_t ctx, char *line) rc = gpgsm_delete (ctrl, list); free_strlist (list); - /* close and reset the fd */ - close_message_fd (ctrl); + /* close and reset the fp and the fds */ + close_message_fp (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -877,19 +910,18 @@ static gpg_error_t cmd_message (assuan_context_t ctx, char *line) { int rc; - gnupg_fd_t sysfd; - int fd; + gnupg_fd_t fd; + estream_t fp; ctrl_t ctrl = assuan_get_pointer (ctx); - rc = assuan_command_parse_fd (ctx, line, &sysfd); + rc = assuan_command_parse_fd (ctx, line, &fd); if (rc) return rc; - - fd = translate_sys2libc_fd (sysfd, 0); - if (fd == -1) + fp = open_stream_nc (fd, "r"); + if (!fp) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - ctrl->server_local->message_fd = fd; + ctrl->server_local->message_fp = fp; return 0; } @@ -1024,14 +1056,14 @@ do_listkeys (assuan_context_t ctx, char *line, int mode) if (ctrl->server_local->list_to_output) { - int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); + gnupg_fd_t outfd = assuan_get_output_fd (ctx); - if ( outfd == -1 ) + if ( outfd == GNUPG_INVALID_FD ) { free_strlist (list); return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); } - fp = es_fdopen_nc (outfd, "w"); + fp = open_stream_nc (outfd, "w"); if (!fp) { free_strlist (list); @@ -1100,24 +1132,24 @@ static gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int inp_fd, out_fd; + gnupg_fd_t inp_fd, out_fd; estream_t in_stream, out_stream; int rc; (void)line; - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); - in_stream = es_fdopen_nc (inp_fd, "r"); + in_stream = open_stream_nc (inp_fd, "r"); if (!in_stream) return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed"); - out_stream = es_fdopen_nc (out_fd, "w"); + out_stream = open_stream_nc (out_fd, "w"); if (!out_stream) { es_fclose (in_stream); @@ -1148,7 +1180,7 @@ static gpg_error_t cmd_getauditlog (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); - int out_fd; + gnupg_fd_t out_fd; estream_t out_stream; int opt_data, opt_html; int rc; @@ -1169,11 +1201,11 @@ cmd_getauditlog (assuan_context_t ctx, char *line) } else { - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); - out_stream = es_fdopen_nc (out_fd, "w"); + out_stream = open_stream_nc (out_fd, "w"); if (!out_stream) { return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed"); @@ -1441,7 +1473,7 @@ gpgsm_server (certlist_t default_recplist) assuan_set_pointer (ctx, &ctrl); ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); ctrl.server_local->assuan_ctx = ctx; - ctrl.server_local->message_fd = -1; + ctrl.server_local->message_fp = NULL; ctrl.server_local->list_internal = 1; ctrl.server_local->list_external = 0; ctrl.server_local->default_recplist = default_recplist; @@ -40,20 +40,12 @@ /* Hash the data and return if something was hashed. Return -1 on error. */ static int -hash_data (int fd, gcry_md_hd_t md) +hash_data (estream_t fp, gcry_md_hd_t md) { - estream_t fp; char buffer[4096]; int nread; int rc = 0; - fp = es_fdopen_nc (fd, "rb"); - if (!fp) - { - log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); - return -1; - } - do { nread = es_fread (buffer, 1, DIM(buffer), fp); @@ -62,32 +54,22 @@ hash_data (int fd, gcry_md_hd_t md) while (nread); if (es_ferror (fp)) { - log_error ("read error on fd %d: %s\n", fd, strerror (errno)); + log_error ("read error on fd %p: %s\n", fp, strerror (errno)); rc = -1; } - es_fclose (fp); return rc; } static int -hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer) +hash_and_copy_data (estream_t fp, gcry_md_hd_t md, ksba_writer_t writer) { gpg_error_t err; - estream_t fp; char buffer[4096]; int nread; int rc = 0; int any = 0; - fp = es_fdopen_nc (fd, "rb"); - if (!fp) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); - return tmperr; - } - do { nread = es_fread (buffer, 1, DIM(buffer), fp); @@ -107,9 +89,9 @@ hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer) if (es_ferror (fp)) { rc = gpg_error_from_syserror (); - log_error ("read error on fd %d: %s\n", fd, strerror (errno)); + log_error ("read error on fp %p: %s\n", fp, strerror (errno)); } - es_fclose (fp); + if (!any) { /* We can't allow signing an empty message because it does not @@ -622,7 +604,7 @@ write_detached_signature (ctrl_t ctrl, const void *blob, size_t bloblen, be used if the value of this argument is NULL. */ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, - int data_fd, int detached, estream_t out_fp) + estream_t data_fp, int detached, estream_t out_fp) { gpg_error_t err; int i; @@ -950,7 +932,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, unsigned char *digest; size_t digest_len; - if (!hash_data (data_fd, data_md)) + if (!hash_data (data_fp, data_md)) audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { @@ -1032,7 +1014,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, log_assert (!detached); - err = hash_and_copy_data (data_fd, data_md, writer); + err = hash_and_copy_data (data_fp, data_md, writer); if (err) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); diff --git a/sm/verify.c b/sm/verify.c index f5d7341ef..39226ed28 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -53,21 +53,12 @@ strtimestamp_r (ksba_isotime_t atime) /* Hash the data for a detached signature. Returns 0 on success. */ static gpg_error_t -hash_data (int fd, gcry_md_hd_t md) +hash_data (estream_t fp, gcry_md_hd_t md) { gpg_error_t err = 0; - estream_t fp; char buffer[4096]; int nread; - fp = es_fdopen_nc (fd, "rb"); - if (!fp) - { - err = gpg_error_from_syserror (); - log_error ("fdopen(%d) failed: %s\n", fd, gpg_strerror (err)); - return err; - } - do { nread = es_fread (buffer, 1, DIM(buffer), fp); @@ -77,20 +68,20 @@ hash_data (int fd, gcry_md_hd_t md) if (es_ferror (fp)) { err = gpg_error_from_syserror (); - log_error ("read error on fd %d: %s\n", fd, gpg_strerror (err)); + log_error ("read error on fp %p: %s\n", fp, gpg_strerror (err)); } - es_fclose (fp); return err; } -/* Perform a verify operation. To verify detached signatures, DATA_FD - must be different than -1. With OUT_FP given and a non-detached +/* Perform a verify operation. To verify detached signatures, DATA_FP + must be different than NULL. With OUT_FP given and a non-detached signature, the signed material is written to that stream. */ int -gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) +gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp, + estream_t out_fp) { int i, rc; gnupg_ksba_io_t b64reader = NULL; @@ -106,7 +97,6 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) const char *algoid; int algo; int is_detached, maybe_detached; - estream_t in_fp = NULL; char *p; audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY); @@ -114,7 +104,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) /* Although we detect detached signatures during the parsing phase, * we need to know it earlier and thus accept the caller idea of * what to verify. */ - maybe_detached = (data_fd != -1); + maybe_detached = (data_fp != NULL); kh = keydb_new (ctrl); if (!kh) @@ -125,14 +115,6 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } - in_fp = es_fdopen_nc (in_fd, "rb"); - if (!in_fp) - { - rc = gpg_error_from_syserror (); - log_error ("fdopen() failed: %s\n", strerror (errno)); - goto leave; - } - rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) @@ -242,7 +224,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } if (is_detached) { - if (data_fd == -1) + if (!data_fp) { log_info ("detached signature w/o data " "- assuming certs-only\n"); @@ -250,7 +232,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } else audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, - hash_data (data_fd, data_md)); + hash_data (data_fp, data_md)); } else { @@ -275,7 +257,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } } - if (data_fd != -1 && !is_detached) + if (data_fp && !is_detached) { log_error ("data given for a non-detached signature\n"); rc = gpg_error (GPG_ERR_CONFLICT); @@ -315,7 +297,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA - && data_fd == -1 && is_detached) + && !data_fp && is_detached) { log_info ("certs-only message accepted\n"); rc = 0; @@ -749,7 +731,6 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); - es_fclose (in_fp); if (rc) { diff --git a/tests/cms/samplemsgs/pdf-signature-sample.pdf b/tests/cms/samplemsgs/pdf-signature-sample.pdf Binary files differnew file mode 100644 index 000000000..0ed04b577 --- /dev/null +++ b/tests/cms/samplemsgs/pdf-signature-sample.pdf diff --git a/tests/gpgscm/ffi.c b/tests/gpgscm/ffi.c index ce18e0794..36b0b98d2 100644 --- a/tests/gpgscm/ffi.c +++ b/tests/gpgscm/ffi.c @@ -42,6 +42,9 @@ #endif #include "../../common/util.h" +#ifdef HAVE_W32_SYSTEM +#define NEED_STRUCT_SPAWN_CB_ARG +#endif #include "../../common/exechelp.h" #include "../../common/sysutils.h" @@ -644,6 +647,7 @@ static struct foreign_object_vtable es_object_vtable = es_object_to_string, }; +#if 0 static pointer es_wrap (scheme *sc, estream_t stream) { @@ -655,6 +659,7 @@ es_wrap (scheme *sc, estream_t stream) box->closed = 0; return sc->vptr->mk_foreign_object (sc, &es_object_vtable, box); } +#endif static struct es_object_box * es_unwrap (scheme *sc, pointer object) @@ -753,25 +758,166 @@ do_es_write (scheme *sc, pointer args) } - /* Process handling. */ +struct proc_object_box +{ + gnupg_process_t proc; +}; + +static void +proc_object_finalize (scheme *sc, void *data) +{ + struct proc_object_box *box = data; + (void) sc; + + if (!box->proc) + gnupg_process_release (box->proc); + xfree (box); +} + +static void +proc_object_to_string (scheme *sc, char *out, size_t size, void *data) +{ + struct proc_object_box *box = data; + (void) sc; + + snprintf (out, size, "#proc %p", box->proc); +} + +static struct foreign_object_vtable proc_object_vtable = + { + proc_object_finalize, + proc_object_to_string, + }; + static pointer -do_spawn_process (scheme *sc, pointer args) +proc_wrap (scheme *sc, gnupg_process_t proc) +{ + struct proc_object_box *box = xmalloc (sizeof *box); + if (box == NULL) + return sc->NIL; + + box->proc = proc; + return sc->vptr->mk_foreign_object (sc, &proc_object_vtable, box); +} + +static struct proc_object_box * +proc_unwrap (scheme *sc, pointer object) +{ + (void) sc; + + if (! is_foreign_object (object)) + return NULL; + + if (sc->vptr->get_foreign_object_vtable (object) != &proc_object_vtable) + return NULL; + + return sc->vptr->get_foreign_object_data (object); +} + +#define CONVERSION_proc(SC, X) proc_unwrap (SC, X) +#define IS_A_proc(SC, X) proc_unwrap (SC, X) + + +#define SPAWN_IO_BUFSIZE 4096 + +#ifdef HAVE_W32_SYSTEM +struct rfp { + HANDLE hd; + char *buf; + size_t len; + off_t off; +}; + +static DWORD __attribute__((stdcall)) +read_from_pipe (void *arg) +{ + struct rfp *rfp = arg; + DWORD bytes_read; + + if (rfp->hd == INVALID_HANDLE_VALUE) + goto errout; + + while (1) + { + if (!ReadFile (rfp->hd, rfp->buf + rfp->off, rfp->len - rfp->off, + &bytes_read, NULL)) + { + DWORD ec = GetLastError (); + + if (ec == ERROR_BROKEN_PIPE) + { + CloseHandle (rfp->hd); + rfp->hd = INVALID_HANDLE_VALUE; + break; + } + + goto errout; + } + + if (bytes_read == 0) + /* It may occur, when it writes WriteFile with zero-byte on + the other end of the pipe. */ + continue; + else + { + rfp->off += bytes_read; + if (rfp->off == rfp->len) + { + rfp->len += SPAWN_IO_BUFSIZE; + rfp->buf = xtryrealloc (rfp->buf, rfp->len); + if (rfp->buf == NULL) + goto errout; + } + } + } + + return 0; + + errout: + if (rfp->hd != INVALID_HANDLE_VALUE) + { + CloseHandle (rfp->hd); + rfp->hd = INVALID_HANDLE_VALUE; + } + xfree (rfp->buf); + rfp->buf = NULL; + return 1; +} +#endif + + +static pointer +do_process_spawn_io (scheme *sc, pointer args) { FFI_PROLOG (); pointer arguments; + char *a_input; char **argv; size_t len; unsigned int flags; - + gnupg_process_t proc = NULL; estream_t infp; - estream_t outfp; - estream_t errfp; - pid_t pid; +#ifdef HAVE_W32_SYSTEM + HANDLE out_hd, err_hd; +#else + int out_fd, err_fd; +#endif + char *out_string = NULL; + char *err_string = NULL; + size_t out_len = SPAWN_IO_BUFSIZE; + size_t err_len = SPAWN_IO_BUFSIZE; + off_t out_off = 0; + off_t err_off = 0; + int retcode = -1; + pointer p0, p1, p2; FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args); - FFI_ARG_OR_RETURN (sc, unsigned int, flags, number, args); + FFI_ARG_OR_RETURN (sc, char *, a_input, string, args); + flags = (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDOUT_PIPE + | GNUPG_PROCESS_STDERR_PIPE); FFI_ARGS_DONE_OR_RETURN (sc, args); err = ffi_list2argv (sc, arguments, &argv, &len); @@ -791,38 +937,239 @@ do_spawn_process (scheme *sc, pointer args) fprintf (stderr, "\n"); } - err = gnupg_spawn_process (argv[0], (const char **) &argv[1], - NULL, - flags, - &infp, &outfp, &errfp, &pid); + err = gnupg_process_spawn (argv[0], (const char **) &argv[1], + flags, NULL, NULL, &proc); + err = gnupg_process_get_streams (proc, 0, &infp, NULL, NULL); + + err = es_write (infp, a_input, strlen (a_input), NULL); + es_fclose (infp); + if (err) + { + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); + } + +#ifdef HAVE_W32_SYSTEM + err = gnupg_process_ctl (proc, GNUPG_PROCESS_GET_HANDLES, + NULL, &out_hd, &err_hd); +#else + err = gnupg_process_get_fds (proc, 0, NULL, &out_fd, &err_fd); +#endif + if (err) + { + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); + } + + out_string = xtrymalloc (out_len); + if (out_string == NULL) + goto errout; + + err_string = xtrymalloc (err_len); + if (err_string == NULL) + goto errout; + +#ifdef HAVE_W32_SYSTEM + { + HANDLE h_thread_rfp_err; + struct rfp rfp_out; + struct rfp rfp_err; + DWORD thread_exit_code; + + rfp_err.hd = err_hd; + rfp_err.buf = err_string; + rfp_err.len = err_len; + rfp_err.off = 0; + err_hd = INVALID_HANDLE_VALUE; + err_string = NULL; + + h_thread_rfp_err = CreateThread (NULL, 0, read_from_pipe, (void *)&rfp_err, + 0, NULL); + if (h_thread_rfp_err == NULL) + { + xfree (rfp_err.buf); + CloseHandle (rfp_err.hd); + goto errout; + } + + rfp_out.hd = out_hd; + rfp_out.buf = out_string; + rfp_out.len = out_len; + rfp_out.off = 0; + out_hd = INVALID_HANDLE_VALUE; + out_string = NULL; + + if (read_from_pipe (&rfp_out)) + { + CloseHandle (h_thread_rfp_err); + xfree (rfp_err.buf); + goto errout; + } + + out_string = rfp_out.buf; + out_off = rfp_out.off; + + WaitForSingleObject (h_thread_rfp_err, INFINITE); + GetExitCodeThread (h_thread_rfp_err, &thread_exit_code); + CloseHandle (h_thread_rfp_err); + if (thread_exit_code) + goto errout; + + err_string = rfp_err.buf; + err_off = rfp_err.off; + } +#else + { + fd_set read_fdset; + ssize_t bytes_read; + + while (1) + { + int nfd; + int ret; + + FD_ZERO (&read_fdset); + + if (out_fd >= 0) + FD_SET (out_fd, &read_fdset); + + if (err_fd >= 0) + FD_SET (err_fd, &read_fdset); + + if (out_fd > err_fd) + nfd = out_fd; + else + nfd = err_fd; + + if (nfd == -1) + break; + + ret = select (nfd+1, &read_fdset, NULL, NULL, NULL); + if (ret < 0) + break; + + if (FD_ISSET (out_fd, &read_fdset)) + { + bytes_read = read (out_fd, out_string + out_off, + out_len - out_off); + if (bytes_read == 0) + { + close (out_fd); + out_fd = -1; + } + else if (bytes_read < 0) + goto errout; + else + { + out_off += bytes_read; + if (out_off == out_len) + { + out_len += SPAWN_IO_BUFSIZE; + out_string = xtryrealloc (out_string, out_len); + if (out_string == NULL) + goto errout; + } + } + } + + if (FD_ISSET (err_fd, &read_fdset)) + { + bytes_read = read (err_fd, err_string + err_off, + err_len - err_off); + if (bytes_read == 0) + { + close (err_fd); + err_fd = -1; + } + else if (bytes_read < 0) + goto errout; + else + { + err_off += bytes_read; + if (err_off == err_len) + { + err_len += SPAWN_IO_BUFSIZE; + err_string = xtryrealloc (err_string, err_len); + if (err_string == NULL) + goto errout; + } + } + } + } + } +#endif + + err = gnupg_process_wait (proc, 1); + if (!err) + err = gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &retcode); + + gnupg_process_release (proc); xfree (argv); -#define IMC(A, B) \ - _cons (sc, sc->vptr->mk_integer (sc, (unsigned long) (A)), (B), 1) -#define IMS(A, B) \ - _cons (sc, es_wrap (sc, (A)), (B), 1) - FFI_RETURN_POINTER (sc, IMS (infp, - IMS (outfp, - IMS (errfp, - IMC (pid, sc->NIL))))); -#undef IMS -#undef IMC + + p0 = sc->vptr->mk_integer (sc, (unsigned long)retcode); + p1 = sc->vptr->mk_counted_string (sc, out_string, out_off); + p2 = sc->vptr->mk_counted_string (sc, err_string, err_off); + + xfree (out_string); + xfree (err_string); + + FFI_RETURN_POINTER (sc, _cons (sc, p0, + _cons (sc, p1, + _cons (sc, p2, sc->NIL, 1), 1), 1)); + errout: + xfree (out_string); + xfree (err_string); +#ifdef HAVE_W32_SYSTEM + if (out_hd != INVALID_HANDLE_VALUE) + CloseHandle (out_hd); + if (err_hd != INVALID_HANDLE_VALUE) + CloseHandle (err_hd); +#else + if (out_fd >= 0) + close (out_fd); + if (err_fd >= 0) + close (err_fd); +#endif + gnupg_process_release (proc); + xfree (argv); + FFI_RETURN_ERR (sc, err); +} + +static void +setup_std_fds (struct spawn_cb_arg *sca) +{ + int *std_fds = sca->arg; + +#ifdef HAVE_W32_SYSTEM + sca->hd[0] = std_fds[0] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[0]); + sca->hd[1] = std_fds[1] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[1]); + sca->hd[2] = std_fds[2] == -1? + INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle (std_fds[2]); +#else + sca->fds[0] = std_fds[0]; + sca->fds[1] = std_fds[1]; + sca->fds[2] = std_fds[2]; +#endif } static pointer -do_spawn_process_fd (scheme *sc, pointer args) +do_process_spawn_fd (scheme *sc, pointer args) { FFI_PROLOG (); pointer arguments; char **argv; size_t len; - int infd, outfd, errfd; - - pid_t pid; + int std_fds[3]; + gnupg_process_t proc = NULL; FFI_ARG_OR_RETURN (sc, pointer, arguments, list, args); - FFI_ARG_OR_RETURN (sc, int, infd, number, args); - FFI_ARG_OR_RETURN (sc, int, outfd, number, args); - FFI_ARG_OR_RETURN (sc, int, errfd, number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[0], number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[1], number, args); + FFI_ARG_OR_RETURN (sc, int, std_fds[2], number, args); FFI_ARGS_DONE_OR_RETURN (sc, args); err = ffi_list2argv (sc, arguments, &argv, &len); @@ -839,107 +1186,35 @@ do_spawn_process_fd (scheme *sc, pointer args) fprintf (stderr, "Executing:"); for (p = argv; *p; p++) fprintf (stderr, " '%s'", *p); - fprintf (stderr, "\n"); + fprintf (stderr, " (%d %d %d)\n", std_fds[0], std_fds[1], std_fds[2]); } - err = gnupg_spawn_process_fd (argv[0], (const char **) &argv[1], - infd, outfd, errfd, &pid); + err = gnupg_process_spawn (argv[0], (const char **) &argv[1], + 0, setup_std_fds, std_fds, &proc); xfree (argv); - FFI_RETURN_INT (sc, pid); + FFI_RETURN_POINTER (sc, proc_wrap (sc, proc)); } static pointer -do_wait_process (scheme *sc, pointer args) +do_process_wait (scheme *sc, pointer args) { FFI_PROLOG (); - const char *name; - pid_t pid; + struct proc_object_box *box; int hang; + int retcode = -1; - int retcode; - - FFI_ARG_OR_RETURN (sc, const char *, name, string, args); - FFI_ARG_OR_RETURN (sc, pid_t, pid, number, args); + FFI_ARG_OR_RETURN (sc, struct proc_object_box *, box, proc, args); FFI_ARG_OR_RETURN (sc, int, hang, bool, args); FFI_ARGS_DONE_OR_RETURN (sc, args); - err = gnupg_wait_process (name, pid, hang, &retcode); - if (err == GPG_ERR_GENERAL) - err = 0; /* Let the return code speak for itself. */ - - FFI_RETURN_INT (sc, retcode); -} - - -static pointer -do_wait_processes (scheme *sc, pointer args) -{ - FFI_PROLOG (); - pointer list_names; - char **names; - pointer list_pids; - size_t i, count; - pid_t *pids; - int hang; - int *retcodes; - pointer retcodes_list = sc->NIL; - - FFI_ARG_OR_RETURN (sc, pointer, list_names, list, args); - FFI_ARG_OR_RETURN (sc, pointer, list_pids, list, args); - FFI_ARG_OR_RETURN (sc, int, hang, bool, args); - FFI_ARGS_DONE_OR_RETURN (sc, args); - - if (sc->vptr->list_length (sc, list_names) - != sc->vptr->list_length (sc, list_pids)) - return - sc->vptr->mk_string (sc, "length of first two arguments must match"); - - err = ffi_list2argv (sc, list_names, &names, &count); - if (err == gpg_error (GPG_ERR_INV_VALUE)) - return ffi_sprintf (sc, "%lu%s element of first argument is " - "neither string nor symbol", - (unsigned long) count, - ordinal_suffix ((int) count)); - if (err) - FFI_RETURN_ERR (sc, err); - - err = ffi_list2intv (sc, list_pids, (int **) &pids, &count); - if (err == gpg_error (GPG_ERR_INV_VALUE)) - return ffi_sprintf (sc, "%lu%s element of second argument is " - "not a number", - (unsigned long) count, - ordinal_suffix ((int) count)); - if (err) - FFI_RETURN_ERR (sc, err); - - retcodes = xtrycalloc (sizeof *retcodes, count); - if (retcodes == NULL) - { - xfree (names); - xfree (pids); - FFI_RETURN_ERR (sc, gpg_error_from_syserror ()); - } - - err = gnupg_wait_processes ((const char **) names, pids, count, hang, - retcodes); - if (err == GPG_ERR_GENERAL) - err = 0; /* Let the return codes speak. */ + err = gnupg_process_wait (box->proc, hang); + if (!err) + err = gnupg_process_ctl (box->proc, GNUPG_PROCESS_GET_EXIT_ID, &retcode); if (err == GPG_ERR_TIMEOUT) - err = 0; /* We may have got some results. */ - - for (i = 0; i < count; i++) - retcodes_list = - (sc->vptr->cons) (sc, - sc->vptr->mk_integer (sc, - (long) retcodes[count-1-i]), - retcodes_list); + err = 0; - xfree (names); - xfree (pids); - xfree (retcodes); - FFI_RETURN_POINTER (sc, retcodes_list); + FFI_RETURN_INT (sc, retcode); } - static pointer do_pipe (scheme *sc, pointer args) { @@ -1398,13 +1673,12 @@ ffi_init (scheme *sc, const char *argv0, const char *scriptname, ffi_define_function (sc, make_random_string); /* Process management. */ - ffi_define_function (sc, spawn_process); - ffi_define_function (sc, spawn_process_fd); - ffi_define_function (sc, wait_process); - ffi_define_function (sc, wait_processes); ffi_define_function (sc, pipe); ffi_define_function (sc, inbound_pipe); ffi_define_function (sc, outbound_pipe); + ffi_define_function (sc, process_spawn_io); + ffi_define_function (sc, process_spawn_fd); + ffi_define_function (sc, process_wait); /* estream functions. */ ffi_define_function_name (sc, "es-fclose", es_fclose); diff --git a/tests/gpgscm/scheme.c b/tests/gpgscm/scheme.c index bde39fcd0..003e85037 100644 --- a/tests/gpgscm/scheme.c +++ b/tests/gpgscm/scheme.c @@ -169,7 +169,11 @@ type_to_string (enum scheme_types typ) case T_SINK: return "sink"; case T_FRAME: return "frame"; } +#ifdef __GNUC__ + __builtin_unreachable (); +#else assert (! "not reached"); +#endif } /* ADJ is enough slack to align cells in a TYPE_BITS-bit boundary */ diff --git a/tests/gpgscm/t-child.scm b/tests/gpgscm/t-child.scm index fd1dcc3fe..461413b9c 100644 --- a/tests/gpgscm/t-child.scm +++ b/tests/gpgscm/t-child.scm @@ -69,37 +69,36 @@ (assert (string=? "" (:stderr r)))) (define (spawn what) - (spawn-process-fd what CLOSED_FD STDOUT_FILENO STDERR_FILENO)) + (process-spawn-fd what CLOSED_FD STDOUT_FILENO STDERR_FILENO)) -(let ((pid0 (spawn `(,(qualify "t-child") "return0"))) - (pid1 (spawn `(,(qualify "t-child") "return0")))) - (assert (equal? '(0 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return0"))) + (proc1 (spawn `(,(qualify "t-child") "return0")))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 0))) -(let ((pid0 (spawn `(,(qualify "t-child") "return1"))) - (pid1 (spawn `(,(qualify "t-child") "return0")))) - (assert (equal? '(1 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return1"))) + (proc1 (spawn `(,(qualify "t-child") "return0")))) + (assert (= (process-wait proc0 #t) 1)) + (assert (= (process-wait proc1 #t) 0))) -(let ((pid0 (spawn `(,(qualify "t-child") "return0"))) - (pid1 (spawn `(,(qualify "t-child") "return77"))) - (pid2 (spawn `(,(qualify "t-child") "return1")))) - (assert (equal? '(0 77 1) - (wait-processes '("child0" "child1" "child2") - (list pid0 pid1 pid2) #t)))) +(let ((proc0 (spawn `(,(qualify "t-child") "return0"))) + (proc1 (spawn `(,(qualify "t-child") "return77"))) + (proc2 (spawn `(,(qualify "t-child") "return1")))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 77)) + (assert (= (process-wait proc2 #t) 1))) (let* ((p (pipe)) - (pid0 (spawn-process-fd + (proc0 (process-spawn-fd `(,(qualify "t-child") "hello_stdout") CLOSED_FD (:write-end p) STDERR_FILENO)) (_ (close (:write-end p))) - (pid1 (spawn-process-fd + (proc1 (process-spawn-fd `(,(qualify "t-child") "cat") (:read-end p) STDOUT_FILENO STDERR_FILENO))) (close (:read-end p)) - (assert - (equal? '(0 0) - (wait-processes '("child0" "child1") (list pid0 pid1) #t)))) + (assert (= (process-wait proc0 #t) 0)) + (assert (= (process-wait proc1 #t) 0))) (echo " world.") (tr:do diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index db1025bbb..1e6d7fea0 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -81,7 +81,7 @@ ;; Process management. (define CLOSED_FD -1) (define (call-with-fds what infd outfd errfd) - (wait-process (stringify what) (spawn-process-fd what infd outfd errfd) #t)) + (process-wait (process-spawn-fd what infd outfd errfd) #t)) (define (call what) (call-with-fds what CLOSED_FD @@ -92,24 +92,16 @@ (define :stdin car) (define :stdout cadr) (define :stderr caddr) -(define :pid cadddr) (define (call-with-io what in) - (let ((h (spawn-process what 0))) - (es-write (:stdin h) in) - (es-fclose (:stdin h)) - (let* ((out (es-read-all (:stdout h))) - (err (es-read-all (:stderr h))) - (result (wait-process (car what) (:pid h) #t))) - (es-fclose (:stdout h)) - (es-fclose (:stderr h)) - (if (> (*verbose*) 2) - (info "Child" (:pid h) "returned:" - `((command ,(stringify what)) - (status ,result) - (stdout ,out) - (stderr ,err)))) - (list result out err)))) + (let ((proc-result (process-spawn-io what in))) + (if (> (*verbose*) 2) + (info "Child #proc returned:" + `((command ,(stringify what)) + (status ,(car proc-result)) + (stdout ,(cadr proc-result)) + (stderr ,(caddr proc-result))))) + proc-result)) ;; Accessor function for the results of 'call-with-io'. ':stdout' and ;; ':stderr' can also be used. @@ -129,17 +121,6 @@ (throw (:stderr result))))) ;; -;; estream helpers. -;; - -(define (es-read-all stream) - (let loop - ((acc "")) - (if (es-feof stream) - acc - (loop (string-append acc (es-read stream 4096)))))) - -;; ;; File management. ;; (define (file-exists? name) @@ -351,12 +332,8 @@ (define (dump) (write (list procs source sink producer)) (newline)) - (define (add-proc command pid) - (new (cons (list command pid) procs) source sink producer)) - (define (commands) - (map car procs)) - (define (pids) - (map cadr procs)) + (define (add-proc proc) + (new (cons proc procs) source sink producer)) (define (set-source source') (new procs source' sink producer)) (define (set-sink sink') @@ -367,17 +344,19 @@ (new procs source sink producer')))))) +(define (process-wait-list procs hang) + (map (lambda (p) (process-wait p hang)) procs)) + (define (pipe:do . commands) (let loop ((M (pipeM::new '() CLOSED_FD CLOSED_FD #f)) (cmds commands)) (if (null? cmds) (begin (if M::producer (M::producer)) (if (not (null? M::procs)) - (let* ((retcodes (wait-processes (map stringify (M::commands)) - (M::pids) #t)) - (results (map (lambda (p r) (append p (list r))) + (let* ((retcodes (process-wait-list M::procs #t)) + (results (map (lambda (p r) (cons p r)) M::procs retcodes)) - (failed (filter (lambda (x) (not (= 0 (caddr x)))) + (failed (filter (lambda (x) (not (= 0 (cdr x)))) results))) (if (not (null? failed)) (throw failed))))) ; xxx nicer reporting @@ -408,11 +387,11 @@ (define (pipe:spawn command) (lambda (M) (define (do-spawn M new-source) - (let ((pid (spawn-process-fd command M::source M::sink - (if (> (*verbose*) 0) - STDERR_FILENO CLOSED_FD))) + (let ((proc (process-spawn-fd command M::source M::sink + (if (> (*verbose*) 0) + STDERR_FILENO CLOSED_FD))) (M' (M::set-source new-source))) - (M'::add-proc command pid))) + (M'::add-proc proc))) (if (= CLOSED_FD M::sink) (let* ((p (pipe)) (M' (do-spawn (M::set-sink (:write-end p)) (:read-end p)))) @@ -568,8 +547,8 @@ (assert (= (length enqueued) (- i 1))) test))))) - (define (pid->test pid) - (let ((t (filter (lambda (x) (= pid x::pid)) procs))) + (define (proc->test proc) + (let ((t (filter (lambda (x) (eq? proc x::proc)) procs))) (if (null? t) #f (car t)))) (define (wait) (if (null? enqueued) @@ -587,7 +566,7 @@ (if (null? unfinished) (current-environment) (let ((names (map (lambda (t) t::name) unfinished)) - (pids (map (lambda (t) t::pid) unfinished)) + (procs (map (lambda (t) t::proc) unfinished)) (any #f)) (for-each (lambda (test retcode) @@ -597,8 +576,8 @@ (test::report) (sem::release!) (set! any #t))) - (map pid->test pids) - (wait-processes (map stringify names) pids hang)) + (map proc->test procs) + (process-wait-list procs hang)) ;; If some processes finished, try to start new ones. (let loop () @@ -682,7 +661,7 @@ (define (scm setup variant name path . args) ;; Start the process. (define (spawn-scm args' in out err) - (spawn-process-fd `(,*argv0* ,@(verbosity (*verbose*)) + (process-spawn-fd `(,*argv0* ,@(verbosity (*verbose*)) ,(locate-test (test-name path)) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) @@ -691,12 +670,12 @@ (define (binary setup name path . args) ;; Start the process. (define (spawn-binary args' in out err) - (spawn-process-fd `(,(test-name path) + (process-spawn-fd `(,(test-name path) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) (new #f name #f spawn-binary #f #f CLOSED_FD (expect-failure? name))) - (define (new variant name directory spawn pid retcode logfd expect-failure) + (define (new variant name directory spawn proc retcode logfd expect-failure) (package ;; XXX: OO glue. @@ -721,7 +700,7 @@ ;; Has the test been started yet? (define (started?) - (number? pid)) + proc) (define (open-log-file) (unless log-file-name @@ -738,26 +717,26 @@ (letfd ((log (open-log-file))) (with-working-directory directory (let* ((p (inbound-pipe)) - (pid' (spawn args 0 (:write-end p) (:write-end p)))) + (proc' (spawn args 0 (:write-end p) (:write-end p)))) (close (:write-end p)) (splice (:read-end p) STDERR_FILENO log) (close (:read-end p)) - (set! pid pid') - (set! retcode (wait-process name pid' #t))))) + (set! proc proc') + (set! retcode (process-wait proc' #t))))) (report) (current-environment)) (define (run-sync-quiet . args) (set-start-time!) (with-working-directory directory - (set! pid (spawn args CLOSED_FD CLOSED_FD CLOSED_FD))) - (set! retcode (wait-process name pid #t)) + (set! proc (spawn args CLOSED_FD CLOSED_FD CLOSED_FD))) + (set! retcode (process-wait proc #t)) (set-end-time!) (current-environment)) (define (run-async . args) (set-start-time!) (let ((log (open-log-file))) (with-working-directory directory - (set! pid (spawn args CLOSED_FD log log))) + (set! proc (spawn args CLOSED_FD log log))) (set! logfd log)) (current-environment)) (define (status) diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am index 7998d2ab9..d1f04e99b 100644 --- a/tests/openpgp/Makefile.am +++ b/tests/openpgp/Makefile.am @@ -212,7 +212,23 @@ priv_keys = privkeys/50B2D4FA4122C212611048BC5FC31BD44393626E.asc \ privkeys/C6A6390E9388CDBAD71EAEA698233FE5E04F001E.asc \ privkeys/D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc \ privkeys/891067FFFC6D67D37BD4BFC399191C5F3989D1B5.key \ - privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key + privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key \ + privkeys/DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93.key \ + privkeys/2F4CD0990D56D41A74456668469E3139A7960CD4.key \ + privkeys/8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7.key \ + privkeys/F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59.key \ + privkeys/8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859.key \ + privkeys/A1598F57316F7FEC3F946895E35A7D2EAE8D3A13.key \ + privkeys/19C87B74004E9839F3D56992B0A9943BF90B56F7.key \ + privkeys/513906BEA5A40F25C9D6EBBCEF62D0784E7235A5.key \ + privkeys/6EC551A7895031EE4543A1C789E16E6A6C229CFC.key \ + privkeys/702F599E35E6E0BE68E6FDF25D887229D42780F7.key \ + privkeys/7C31A4A632A49C4E8B1C8CBA53976ADFF714510F.key \ + privkeys/A1ABFD89944870D04039D40C218EE127254AEEE9.key \ + privkeys/A87B85D88DB8B2B5A62A9958C8F2878F49605D09.key \ + privkeys/D54E9B75C3541D95C45E430DAC9645E9FB62C668.key \ + privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key + sample_keys = samplekeys/README \ samplekeys/ecc-sample-1-pub.asc \ @@ -239,7 +255,12 @@ sample_keys = samplekeys/README \ samplekeys/ssh-rsa.key \ samplekeys/issue2346.gpg \ samplekeys/authenticate-only.pub.asc \ - samplekeys/authenticate-only.sec.asc + samplekeys/authenticate-only.sec.asc \ + samplekeys/pqc-sample-1.key.asc \ + samplekeys/pqc-sample-2.key.asc \ + samplekeys/pqc-sample-3.key.asc \ + samplekeys/pqc-sample-4.key.asc \ + samplekeys/pqc-sample-5.key.asc sample_msgs = samplemsgs/clearsig-1-key-1.asc \ samplemsgs/clearsig-2-keys-1.asc \ @@ -272,7 +293,12 @@ sample_msgs = samplemsgs/clearsig-1-key-1.asc \ samplemsgs/signed-1-key-1.asc \ samplemsgs/signed-1-key-2.asc \ samplemsgs/signed-2-keys-1.asc \ - samplemsgs/signed-2-keys-2.asc + samplemsgs/signed-2-keys-2.asc \ + samplemsgs/pqc-sample-1.enc.asc \ + samplemsgs/pqc-sample-2.enc.asc \ + samplemsgs/pqc-sample-3.enc.asc \ + samplemsgs/pqc-sample-4.enc.asc \ + samplemsgs/pqc-sample-5.enc.asc EXTRA_DIST = defs.scm trust-pgp/common.scm $(XTESTS) $(TEST_FILES) \ mkdemodirs signdemokey $(priv_keys) $(sample_keys) \ diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm index bf3714f50..1ac25bf65 100644 --- a/tests/openpgp/defs.scm +++ b/tests/openpgp/defs.scm @@ -268,13 +268,14 @@ (define (gpg-pipe args0 args1 errfd) (lambda (source sink) (let* ((p (pipe)) - (task0 (spawn-process-fd `(,@GPG ,@args0) + (task0 (process-spawn-fd `(,@GPG ,@args0) source (:write-end p) errfd)) (_ (close (:write-end p))) - (task1 (spawn-process-fd `(,@GPG ,@args1) + (task1 (process-spawn-fd `(,@GPG ,@args1) (:read-end p) sink errfd))) (close (:read-end p)) - (wait-processes (list GPG GPG) (list task0 task1) #t)))) + (process-wait task0 #t) + (process-wait task1 #t)))) (setenv "GPG_AGENT_INFO" "" #t) (setenv "GNUPGHOME" (getcwd) #t) diff --git a/tests/openpgp/privkeys/19C87B74004E9839F3D56992B0A9943BF90B56F7.key b/tests/openpgp/privkeys/19C87B74004E9839F3D56992B0A9943BF90B56F7.key new file mode 100644 index 000000000..d0c602d3f --- /dev/null +++ b/tests/openpgp/privkeys/19C87B74004E9839F3D56992B0A9943BF90B56F7.key @@ -0,0 +1,7 @@ +Created: 20240423T121650 +Key: (private-key (ecc (curve brainpoolP384r1)(q + #0472FB0D5A0A01E55C29E9FB8C5C425BDF37150DAFA3C556C786E2FEF9E011919E68 + 3DBC7731D1281FDB9780C4B7FD7785198516BE2033D06448BA7EA39C2BCB7128BC1E0B + 3F81F2E734434E6FE96B29E19C57B423C5009134010CD87FADCA63A1#)(d + #474FD16712E9A8EC87A6F94E553E369358985475B453E95CFFD2123E4E97679720AA + 269CD6002DC688C9F3B9B8C456F1#))) diff --git a/tests/openpgp/privkeys/2F4CD0990D56D41A74456668469E3139A7960CD4.key b/tests/openpgp/privkeys/2F4CD0990D56D41A74456668469E3139A7960CD4.key new file mode 100644 index 000000000..b35c7377a --- /dev/null +++ b/tests/openpgp/privkeys/2F4CD0990D56D41A74456668469E3139A7960CD4.key @@ -0,0 +1,5 @@ +Created: 20240419T125003 +Key: (private-key (ecc (curve Curve25519)(flags djb-tweak)(q + #406438411B2E3EA2EA48B681860C2CA537978C69072CBD04A40069E122660F6F37#) + (d #7D295F7111BD111DB6685B451EF1FCFEA61F7777B8227194F97E093D6E508268#) + )) diff --git a/tests/openpgp/privkeys/513906BEA5A40F25C9D6EBBCEF62D0784E7235A5.key b/tests/openpgp/privkeys/513906BEA5A40F25C9D6EBBCEF62D0784E7235A5.key new file mode 100644 index 000000000..c902ecf25 --- /dev/null +++ b/tests/openpgp/privkeys/513906BEA5A40F25C9D6EBBCEF62D0784E7235A5.key @@ -0,0 +1,8 @@ +Created: 20240423T151658 +Key: (private-key (ecc (curve brainpoolP512r1)(q + #046E26896EAC8D3D7F31F0B57439FFDBC0078841CEF7A9A98AB15F489FEE34E9D15E + 2050EAFCA0CD4C7021E5E018F601EEC7EDDE1AACE959EA13F84143861489DD54ADAA4F + F86E5FF75E3CC2EA6453716075DD908B9647B45257A64AE88DD390D7325B9E30698027 + 16B3743AFE7A7E44495AF625C3E009C581C63E341A16A23D07#)(d + #1A1364149C7AB54D76D9345424EF755139031E85B5B7DEB0D221855D0189A579614B + BCB6D01D9E02627F5C187338D7A176A830DF55422FE20D3BFF7812255C1A#))) diff --git a/tests/openpgp/privkeys/6EC551A7895031EE4543A1C789E16E6A6C229CFC.key b/tests/openpgp/privkeys/6EC551A7895031EE4543A1C789E16E6A6C229CFC.key new file mode 100644 index 000000000..e9df95c0a --- /dev/null +++ b/tests/openpgp/privkeys/6EC551A7895031EE4543A1C789E16E6A6C229CFC.key @@ -0,0 +1,137 @@ +Created: 20240423T151658 +Key: (private-key (kyber1024 (p #BE7768B33800B583978414965EC439D29A4180 + 655B0A58BA40CB9C29D10088A07D66B47AE6124B986156A9347878FBCFC0351049D2C1 + 8835CEE72BADE28B7AC8BA7FF8345AD885B1E730409939C5B3A1A5867AB7912538F234 + 59AA335204ECC5E94A9B651772A9AC5C60A5137AA74719996479D7A424839172398078 + 57493825896C628DA1726418D843E4946F9838A2E2DA5B93C6805995CD7D4B26EB79BC + BD645E52B31703986686CB2AB26A22029879BD57212928096F0954A65654FAD6651FEB + 11039C6E0F03172025303480AE6F5A0ED786A17E3779ABD775B15407B6560C97C42CF8 + 6A63D7AA1032767D20D97EAD7A3EFCD346D00B4DFD147FAE9C7BCC64224837B6C62770 + 76E25BE756C201A58445779AE98679C753C2BBA69425D343A5156EAED17AACF0B939C4 + 9B3FE83AA80C5E4399BE81C13F0A2CB4E6B08E588A13B8944AC9F74D79541FAB1691BE + 88156F920FA836C4D41AC4965009FAA174DA36B5521C79F4313B498525311369261B64 + 48FC7AEE6B9D44455BF6347C35F2B99F95254C6027759B99CA16659006BAD1F7C0B20C + BDBFF66A8F454E24863B175A0B614830AC8ACB1AFB93F1D3C54F7216DAA05AE210B57B + 503E4657B6870A8CD448644B7A5B68F565E9F51C9F12890AC8315ED52015F6BBED89C6 + 924BC756EB752FB3502BC50CAFA073E1B6AE3DD29E0C9269F8B505CA0654702114F03B + AE81C945B13631EA6C28B244BCDFB159B55B1EACAC30AF7872BD3C8F5BEA0EE9056DB2 + E6C87C9B204AB21018696217D648EFCBBB4C078C16751B03702BAF447FFB741FDB2455 + AF5A9BCA6A3340C5B8F6B82748723024B20E6F5A3D56278DBC975D4EA70D9399A6E355 + 533920A3C5E858AD5180BD4434F3E42B971908AA591ABBF3C1EBF257627CB2BF740DC1 + B88C7551A5B8462F6CE3B58C65A390C07C01A15C2BC624AB007924D9B76DD1A50603C1 + 92278A24CC86CFAA952E66ACFE99A800D56C473A7877955FC7D83C1DD548A57527B259 + 0B56B6CB3E8525F06986725379C4130F8FE15524D569346AAF451538DC663149F1946D + 5509BE3BB6026031F87AC4B21B80D66981F8527CDAF0AD4BFC2A3B3265BDAC4E65190B + 5D777275896EA238606CA7B2173C1C4DE01058965128BC866B8CC2991045AE47324243 + CBB0808E2F851FC9563779AC269664B01AECBEF45465FEB9584CC47F3B031794594BF5 + 7497446B0A856755429A7389A21AB3573C2D8A036238486451013D647D288979200A83 + 72C6039FA23E35877008408C7C815E55035A5BA9165BBB3BA56A96253B526DDC37FCC2 + 9F16F18E7D6676A393B867849C85927CAF996A9FE388FF880D5615AC64B7B4251803D7 + 9599D2232D560C85F562643E9B9F33F8139D0B61ABE98F76043E5166145A045B83E574 + AAB35D9D119A3EB3107FB3388EF483B056726B3036C29663018C929FD972D444C54373 + 16D749AF0D12556F159FC02584C6B8456BF56DC2128DCA61B794E7AAFB61969354B5A6 + DA82EB545CFA242CE997A5168AA43926131165B534FBBE1F15A4D4432FF5BB8446A575 + E528480B7C3FA0328C8134C0BF778792C05902EB32476326AE979CCF0C80C39C4B7332 + 9FC4EA365164A848D529BCB43985D35B3AE30B02AC057E1910BDB616F50A79F22441B3 + 51C77C8A17AB4052F44930B991AF7C751209253C4D27C8C5166C3285A1B1D39981B85E + CE5CC7AF11B4B8B38170963766E6130A66ABD736478067C0A86470C542216AE1593591 + 62F72C34241601E4F54087D64A8752C38FD85CC09C0C7F38CE3A0320E5E16B507302D7 + 830EA18793774A3D7BAA807971AD17332D8862C0FA757D98782CE927577A32B45BF18E + EE77B393F410620C03093620470BBC93313489F11C96871C42DA296909ABF16A885950 + A2CF896CD18A61D54B3C529C96355AC2D3C69324C3AE634C8718C3CBFA734C40E78F91 + 53A1286B379E0B7636141DD20B43A26B6BB2E03A56F008F0868CE2B940CF108FCE7954 + C0A1B933CC15FE156E2C4C4E4EDCBBA1E87108E3CFF24865548A838E7619315B25C265 + 3465158CAE4683AA03696870C397359B118CB37AA853D46CC5B05ACFDBC010C8984284 + 07009340A4793A05718B0E12E95FA8685A213B8559B44021B8C3A458AF81C2476775AB + F0015DBFF820427CA306265DE9AE0F4AE474DF627DCD167AEA9FB7A6EB3F021BA4A78A + E111275F13AC923E23#)(s #2EB39309148C404770B9EC5188D13FEDD24E3DAA78FCC3 + 5AAA6840B1458E30511A973C389FF3B4F8073ACF9780AC09A648822E460C6399C125B5 + 191DCF16C92C350FFCF25A94E22E4E80C9A25C18DFC884D9536D2E8469CAC88781726C + CE899E477BAFE3B25F3319A310E499AF030CC65867E207D0BCFA1C1EF711F2403B8293 + 7F83A0859051CDC9AA9B43B53197CB47B1F47BC6BB5A073A3D5B2182A312C747A50C5B + E67CB43315019CB0B6D73216943144A5228307A4D7FA3C88B0B3AA1B088CCC73A81AA3 + 0EF56D06D3A2139517215A843E093C1F61C1B1890FE5E9A0FC64986622BBFF7635F6F7 + 70C1325727300665B437544456C1211FC7D1753B724DB691CAE729BC89CA97B5C60363 + 710FF5A2502C54C429591824A62547D70FB17A8D177979473B254E12BA1A589B1039CC + 9AF061FF098AF5B09B89334DF3E54B6D524FB8B99F7C9A41F4731175D4904BA040A201 + 76BF153241FC5F38D63665629C75C97935F99DF7B7B415816705C00A47020DB2D8A6C7 + 945FD950206FB1CE970A973A285469FC6B2D495BCC50BACCCC9DD03A9D85C2B667BB64 + 887A37EB645DC5F4281ABA6FA5D2B73C0B97D9AC9220DB5A0ADABA6B438367E60E0412 + 453E6791C275A19F1B44D23A9C00E11610EABC332746F6FC09DDAAA402475E7528C57F + 8B79BE8B506F100E43AB3131C138225622DC597F0405B86A3CCABF2B45CBA028FE1221 + A9AB4368EA6D9129A80A26B2F8CB76438A15D0FC8BD0B26BD6D5BDAB75BA25F636A889 + 95DCE2656A21C66C3CCBE2CACF38C66F2D66BB55C81A66C1695BA5391BA327F44B6596 + 4A035A37C6C3BA7198F27E758C822EDAC40E50CBC86274E6EC6710B74178321FEAD3B7 + 20319A50D34EAC953424379CCCF9753E3C26D60B896A555C62D21F226899620995CCEA + 312F840D08294A6816789DC77B1D730151FA5084BB8CA3CBA402E834CA5C2069D6455B + 133864F64CCDAA7C98B7038AD6A1FCF24C81E6B20D2C7BA1B1B45B4A949D48AC51683F + A8C4938B4CBBA49C258318742BFAA172DC58C5E628BD9A10A9C54116705B6E9C80D936 + 2C62E26DC5A132F02B2A4B1A2376A74668ECA44D2080C414941C6A3836CA90C590CD3D + 2B74F2C33C82288B15B708F4178D7B1A01990308DC76C0C83794063B0D2F434EE86AAE + 9240163AB0AB49DC36F5AC925F05C38CD6722E70B09F8871F04066DF90A4EF06AB3776 + 161DD8817313653A0A58CCE69040EBB5D84B9B1DEB0892E8071B073965D68F4C191354 + 184E2D0100A272636560C305394185D01D0FE527A66CC1F4ECB34C208222FB505FB39F + 5B47A6CE87BA58C848EAD5ADC78B17CE6584A4336C3AF8C23C9BBE805ACA16AC53247A + 621D074A1AC79339B83661D7018E55A6091C58441606A862C29A79BCB7600041AC7CFF + 5C76961B48D4C47437E3119D2697B28502748036784A99F4D07F21FC16C6A1C5F44070 + C2F07F71FC74D91114C8E71053E18A59EB567C1445CBE3216CAB431FE59D42E627BB8C + 5C905C4FDA2B1C0A5B3B67567879660E044B5FA0666310939250020AAF31A6FA789B38 + 904121713F08A5760EAAC87EA58444D25FC40787A3600B55059FE081AA535BBF6DF25C + 1C4A27DBA8A243AC88A4D879BBAB260BFA62DF0557BF7B7466654DE5C69DDBB18C3E17 + B5EBD0AD91A80AEA71C2F5F076794C1B60323E7C5416F11179CAD8A6F48C943AAB6265 + 6A9AFAFA2F5CE8918935C21E169806463CE140B1CF6930B9A3AD4AFCA1B2607867B441 + 5EE696E3949D53AAA74A8692913AA900E5A32BD7C9482284F1133245A08EBA41C17C70 + 987FC106C3961B3A13C2E9966DC6F9B42CFBA39EBA7620CA6E42DC7F58759315F04703 + 262E334B45CDD90FDE270C679A67C91B8D80076E4F539678822AF993633EC54FE6486D + 53E51DE286C5887B66A6188BC7E88BFF4455C03160F3A2AF2BF9C7EED288D1F977EB69 + 72BA48742543325FB667DA80B5B6CB3C70B76C15940A93CB2ABAC76315F18E7A578370 + 6320299C33B8636E0354966E46585025462444300BFB94C36784904A1DF199C6B1AB62 + 410723D6365F50111CD7854384296D47A1BA7B241FD179443C5721D3655435069C0F6A + 4DD6E21C40F3CAA5502431601486DB3ADC329888D7ACB364B3902B6781B52712C70054 + 8C61F811184A00AEBE7768B33800B583978414965EC439D29A4180655B0A58BA40CB9C + 29D10088A07D66B47AE6124B986156A9347878FBCFC0351049D2C18835CEE72BADE28B + 7AC8BA7FF8345AD885B1E730409939C5B3A1A5867AB7912538F23459AA335204ECC5E9 + 4A9B651772A9AC5C60A5137AA74719996479D7A42483917239807857493825896C628D + A1726418D843E4946F9838A2E2DA5B93C6805995CD7D4B26EB79BCBD645E52B3170398 + 6686CB2AB26A22029879BD57212928096F0954A65654FAD6651FEB11039C6E0F031720 + 25303480AE6F5A0ED786A17E3779ABD775B15407B6560C97C42CF86A63D7AA1032767D + 20D97EAD7A3EFCD346D00B4DFD147FAE9C7BCC64224837B6C6277076E25BE756C201A5 + 8445779AE98679C753C2BBA69425D343A5156EAED17AACF0B939C49B3FE83AA80C5E43 + 99BE81C13F0A2CB4E6B08E588A13B8944AC9F74D79541FAB1691BE88156F920FA836C4 + D41AC4965009FAA174DA36B5521C79F4313B498525311369261B6448FC7AEE6B9D4445 + 5BF6347C35F2B99F95254C6027759B99CA16659006BAD1F7C0B20CBDBFF66A8F454E24 + 863B175A0B614830AC8ACB1AFB93F1D3C54F7216DAA05AE210B57B503E4657B6870A8C + D448644B7A5B68F565E9F51C9F12890AC8315ED52015F6BBED89C6924BC756EB752FB3 + 502BC50CAFA073E1B6AE3DD29E0C9269F8B505CA0654702114F03BAE81C945B13631EA + 6C28B244BCDFB159B55B1EACAC30AF7872BD3C8F5BEA0EE9056DB2E6C87C9B204AB210 + 18696217D648EFCBBB4C078C16751B03702BAF447FFB741FDB2455AF5A9BCA6A3340C5 + B8F6B82748723024B20E6F5A3D56278DBC975D4EA70D9399A6E355533920A3C5E858AD + 5180BD4434F3E42B971908AA591ABBF3C1EBF257627CB2BF740DC1B88C7551A5B8462F + 6CE3B58C65A390C07C01A15C2BC624AB007924D9B76DD1A50603C192278A24CC86CFAA + 952E66ACFE99A800D56C473A7877955FC7D83C1DD548A57527B2590B56B6CB3E8525F0 + 6986725379C4130F8FE15524D569346AAF451538DC663149F1946D5509BE3BB6026031 + F87AC4B21B80D66981F8527CDAF0AD4BFC2A3B3265BDAC4E65190B5D777275896EA238 + 606CA7B2173C1C4DE01058965128BC866B8CC2991045AE47324243CBB0808E2F851FC9 + 563779AC269664B01AECBEF45465FEB9584CC47F3B031794594BF57497446B0A856755 + 429A7389A21AB3573C2D8A036238486451013D647D288979200A8372C6039FA23E3587 + 7008408C7C815E55035A5BA9165BBB3BA56A96253B526DDC37FCC29F16F18E7D6676A3 + 93B867849C85927CAF996A9FE388FF880D5615AC64B7B4251803D79599D2232D560C85 + F562643E9B9F33F8139D0B61ABE98F76043E5166145A045B83E574AAB35D9D119A3EB3 + 107FB3388EF483B056726B3036C29663018C929FD972D444C5437316D749AF0D12556F + 159FC02584C6B8456BF56DC2128DCA61B794E7AAFB61969354B5A6DA82EB545CFA242C + E997A5168AA43926131165B534FBBE1F15A4D4432FF5BB8446A575E528480B7C3FA032 + 8C8134C0BF778792C05902EB32476326AE979CCF0C80C39C4B73329FC4EA365164A848 + D529BCB43985D35B3AE30B02AC057E1910BDB616F50A79F22441B351C77C8A17AB4052 + F44930B991AF7C751209253C4D27C8C5166C3285A1B1D39981B85ECE5CC7AF11B4B8B3 + 8170963766E6130A66ABD736478067C0A86470C542216AE159359162F72C34241601E4 + F54087D64A8752C38FD85CC09C0C7F38CE3A0320E5E16B507302D7830EA18793774A3D + 7BAA807971AD17332D8862C0FA757D98782CE927577A32B45BF18EEE77B393F410620C + 03093620470BBC93313489F11C96871C42DA296909ABF16A885950A2CF896CD18A61D5 + 4B3C529C96355AC2D3C69324C3AE634C8718C3CBFA734C40E78F9153A1286B379E0B76 + 36141DD20B43A26B6BB2E03A56F008F0868CE2B940CF108FCE7954C0A1B933CC15FE15 + 6E2C4C4E4EDCBBA1E87108E3CFF24865548A838E7619315B25C2653465158CAE4683AA + 03696870C397359B118CB37AA853D46CC5B05ACFDBC010C898428407009340A4793A05 + 718B0E12E95FA8685A213B8559B44021B8C3A458AF81C2476775ABF0015DBFF820427C + A306265DE9AE0F4AE474DF627DCD167AEA9FB7A6EB3F021BA4A78AE111275F13AC923E + 23D1F7082DD2B7C9BA1CF7957573C9778E452967D9D35277277471D1E15EC88ED243E0 + ABDAF1149BF31CEC85D81BB32BF3CFFB4E62301100C95B53023559948E18#))) diff --git a/tests/openpgp/privkeys/702F599E35E6E0BE68E6FDF25D887229D42780F7.key b/tests/openpgp/privkeys/702F599E35E6E0BE68E6FDF25D887229D42780F7.key new file mode 100644 index 000000000..cb3e6ea3e --- /dev/null +++ b/tests/openpgp/privkeys/702F599E35E6E0BE68E6FDF25D887229D42780F7.key @@ -0,0 +1,7 @@ +Created: 20240423T121603 +Key: (private-key (ecc (curve brainpoolP384r1)(q + #044F9318AAA2E3A3D28DA76F4B3B6B7CCAFA9B77A571E9A5BFFDEAC24A0FD96C6BB3 + 8F74FCF980696EDD5F4CBCA2B628AE24C9DBC1C60EF1D5809D4D544EBAA01F7744233E + 248106D98A67CE1ED52D14FA942F6090C9988AA5EEB2368E19F679E2#)(d + #0D146C6EDEC6AE142765DABBFFEF4EA6CA290EB7DDF99676F3F59AE6CA3942531B31 + 7330A07F5C8AAAEDFF69E6855301#))) diff --git a/tests/openpgp/privkeys/7C31A4A632A49C4E8B1C8CBA53976ADFF714510F.key b/tests/openpgp/privkeys/7C31A4A632A49C4E8B1C8CBA53976ADFF714510F.key new file mode 100644 index 000000000..e46ebbc6e --- /dev/null +++ b/tests/openpgp/privkeys/7C31A4A632A49C4E8B1C8CBA53976ADFF714510F.key @@ -0,0 +1,137 @@ +Created: 20240423T121650 +Key: (private-key (kyber1024 (p #9A9A0790782C7F4A7F51934777FBCF7E56C66C + 2A56158680E424196D2749B7D624FDC9AC2B890BF8594CAE427E79DB4B0551A1512736 + ACA58CA8ACB41DDB854A08B546170190075051E91A85529795044500E239514A2ABAD1 + BE0D4C9F6512A887C30903AABECFDB6A6708185641402473637C80806162BDF85C2133 + B0BBA05450E638860784902616621FE7529A107A5A976FAC4985C3583BEFF33DC4A05E + 52767599D426C11BBDE0A0C0BC45B6AEC3747A316E3119ADA71A5B6589927797483E36 + 13E9F836C87685A26BB35C93CCDCC8B2C3B8AF38C60B2B8BB54B21C9ADC91644D541E1 + 70AE1A97ABCAC321BF41855A48B71CF9250A39948601100AD1B26DA8740E2AA117E60F + B813848A126FDB87C0F2856B0497104250590B9041919A0253ECA5F283688D25C7788B + 68A21450F388B1FFDBB6736A9E38CCBC2A2B0F6A64AD45F882EDAC680AF060C0FA2B73 + 921E7F053EDA54549176BE0E65ABD94B490FE75681822B21B4784D849FB32C76DF08C8 + 17846A2628B1118580818740B637CEF7B31C1EC2B10FA7ADB8B4AEC9B541C568244E74 + A4D94817E7B294B78A637A8AAD9F762028398B757063445C1BEE807B316788A3FCB7F8 + A288C5463975301E4EA27171738EA0983D5AE01CD38811E9699F5065101CCCA251C052 + 4CB5B147B98FF65902243C98B1992475D297910383F6AAC31BE8A32D8A6341F436F5C4 + 928826AD4628C51A443826C05314E11851BC41CA1571E43061BB730A5AA1CBC9963690 + 3745458851E28864E54A885C0BC88C423670795777784A9607A3BD2004F1F51871000D + C6F2578AC434D486871E665D14C76B1A416CC72676A96C4E6F79B75BE23FA31C2527D7 + C976133F4B211E07E0BF4ED46C2FB77D81272290DB760DE8B567527B36316276AA9A36 + 89AF4B3790C08820A844258D139856944EB8C40C9EBB6D199A196D446FCF25B0E61688 + 4591950021AC9D5B62AFF2A46A955C36D21EC41B294EE3AC6235323BA1B3A3000B384B + 87813550FCAA470C6C6CBCC16D8732C5A3387C16C87B34B60D56B71797B177584C6A20 + 804C9C145698A2ADA0453ABB290B36124D2690124D1B4A0FB37BAA9A1BA0237690018B + 66E426B7F1400A8B3C0635A79689861F3B1A8041919F600B435179E375859591293E98 + 209A2B07AFE028982B5532275157E0545F844BC07C0491E55A2757A6DBDBB0A0817D58 + 0CA677E8C60823AEFC76B42845AB54C40BF0D5733198C326AAA99F11667FD583A97195 + D4091BA790C419BB2312684479E15E09D94974B5BF747CCC99BB9F348C411178645555 + 372EE08C514B6AF5BC4EAF357FE0132AF374475BFC7754B29945D525E5E45561967002 + 452C9B6A99B7340577D9AC5184911D3BA1E833C3FCD93B3401B3E8913721179D75AAB9 + 94A17FA6C9C62C2CCE4194C563B4A78BABAE8CC82E177835AB351884FA1638898D5491 + 2A2E87A5BF14170FD689E0E494DED76C43EBC23CE564B0EB94A5927DEC720C98346562 + D375E116AE6E00B5081B69CC45AB8263ADA4304C50615FCB3C78FA37961953560912B0 + 1D2C255541B94C929E1F105B6F462A36436090461473ECAA05BCCCB26536DFC157EA60 + B37B49B4A17154AE6003A1C053E52329DF375F86A48300B7855C6865A7FAAA3F04A10D + 7821B18A978B8AA8DD096C6C101015C4CF385422CD1105E765201A445D9D22B3C61525 + C4A09C692CA838E01EEF6B45F82A25B9F15108441A5911C5529498BB629A22543239EA + 9765453633C51E75B8C62F2C2D5E8A524E69097DF8480E8347F0F94ECC193DD9E2C86B + 7134066189C7FA6BC12331D5622CCAC42D090553E1191186E6315165BAD5039AA3A75A + 777AB9057A59D463C0E93B5DC34A058519848C78A607ECC631CC050554CEA61B29658A + 75D4D33DBC95AF5E587C1EC3B2B8E717FE85B8D3D41AE5C4700DCC449EA65491B55685 + 791686CB9B51E97B18671D92F1A356A3060001CAB220707206B485EA6E0BD85F53719D + FA3876C6534058C4C291330244C19AC87B944B6945B77B44577772DDC8727C60260C5B + 17B4819A2C801A9D168D0EEA03BCA50236BC20F69886CEFB0C6994BF25C1CC09209EA5 + 3B6C04F15F9613C5D04646D151457A206F0B35C9A40CC3782B2F851759F93040CC0BC9 + 75477C7228BD7D1435DA80607EAB07CAF93F3FAFC623B4458888F05949EA9B2972888F + D40F3E6E009F8C935E#)(s #8D204312E102743757098841C5E7661CD0469E50326578 + B3F7324E3A303617BBBFB9022E03C61F69BAA89890C72AB82964B60F039786C9436A84 + 7223E412A76F34B1AB913C44D0CC9682593BAB0738072748F2014A38A424DA91D629A4 + 33DC33928C862BBC466B3166884010AD6A33E20A75D9870D3C07C183C01F410444D982 + A15B8A377806136DB03A94EB5D2EF1311AE03884E92CED483365C582E5C60EE708AF20 + 939E135A5EE45781448911DAA90EBCA6792F86C13579C4D6305816F493A35C4F8BC3C6 + 4D516D901A38174701B33C597AA40953FAB5CF3C672C4B29220176BE370846AB24E216 + B634F3A6ED26CA88757854883FFB203382DB609F473686E57AF1970B75724E8D339DF8 + A7B614063556E72569447A81901705A319686125EE5480FB8C3C1B73CDE6DC8EFE7060 + 2D8811C52C85DB834C06B57850AC742683C05451B27F8B47C7491A42B102D1A83AB114 + 0D8EEA401C80467CC1826C59B572DCCA3B32880E07BC14C7A6A39015E7548197FC1BB8 + C8203554312F170FC3916727E9A5A349B427EBCC2833C77F4C045E8C4F5B39708C4C61 + FD2761BD824BCD5606992B1716C15EDE0155DD311238BC529C7838AD6B65BFB31915F6 + 7248021410E6A4BA44817D307B0F713689186BA91C683EAA7C29B509D57A41BA0A09FA + 4427F5C806CD705039669A5871C4170BB3F7081A77E17BFB1455311650EEFCC35C8336 + 03976CC8B34C9C23CA52641698DB36ED45AAABE15540A757F0BB09647B5D0943305FA7 + 91C89C623EC941D24CA283A208EB8745F0B2049E196CB3CC032782BB44F01B94A89883 + B5C52040806BFABA3143464677CF8540876E189FBE69BEA7B151D8106711F666929596 + D61A7344D783A7A741BCC4C5F8A9064FB29C27D8A08EA009E13414523088361B52A6C8 + 5C5FE88D54E4CAC784804F9C0E9B508E49DC7F1FD9574863C2B3B39EE74614BA60ABA6 + 52777832175026613BD08EB979B66B92AFEE89341C30770601892172CC7A34587C04B3 + FB901F0618CFEC0BA54874C95F833C99AC2ED7F20AB74C3719A27D7572CA6F8A99A221 + 843CA939491B210703497CB3288F227680E54F0954B62B455650979250336509709280 + 7BB80349A2B4F8576B4600225A8D059608C8173E78398C971A12940B14F414208ABA1D + 00343DA1332E2A813164D80C8E9B54D0212A481027EC0B81D3580E74982D63DA100265 + 9EB600A9E096140938526AA3B709B9086F1A9E78076EF36B372EF3C4DC4C6A1BE015C6 + 9B7DCFE960AAE67449424BD4448861520EE59008391243C9F508DBF22A7DF1C90DA9AB + FF663DD1D7BA02B2C21DE9BB5656BD935B639565982BE41805D5A2FE63646CB48A4A69 + 26A8E11C3185BAEF032372B8687ECC71021BB44004ADA09A9B6AAC0F335C2A9B689BEB + 556D9C706CCFC480DA20695CD0C94807A05BCAB436B88911FBB36DD36E1F734E66F288 + 5A325A06CCA6CEA3BECCE02E1E6B71FB9A5327A13B172C9E9D908327655A25DA8E6307 + C9BC251C4C7CA11B211327C68783727AB7F280FB654EAE3883BA3B54D2EBB4564A7C23 + 7AA44D3C15F48817A23B9A6D346D562560C34B41CAE117075857F9A6B23B717EBD719F + D4A75537D9420F411C0D9A75BF3160DA172FA9615579E4B0D4D965376B6F5006660330 + 216DB476F2688B512A836D52A2CBDC4C56B690BE205D1C681EB1D55702BC0611B960F6 + 558AACD22B41370C04B10BBC63B4EBE10BBDDC024C94BD7046C525F2767C6A55668A0B + EB47C0330C1A4E17493B15460DA6AD8084C304268DC0203013119017B59C5FE2C785A6 + 6BD7E130004DCEC940BB422CCE6D434049385443E18FC832C0A2C60553A5B4C81B8708 + 78AB143016F883A36A4742ED7B2BE8EAA13C817475F6B0A49462AD38A0993CB1A6F5A3 + 88E0835A8164B5F9B53B8C7A41E419F9E42ABEA1921BC5154B293D00B05CD900323533 + 1384E8AA789304F570249BF2B1D2AC7FD4AB93F76B41E49421ECBA3AA0D44A7A72296A + 5711069C94394270D03B3B13D99CAEB78B091C8C71924D5241ABD14C80FDE9C2E34346 + 8A974A844B760BF80A2826249A22A20E26ABFB1486AAD01E815B54A2CB32E179946C04 + 3F6F298DAB042FEEB65494376784F8424D73099910154F29CF1CC55A4D76BA3487C41F + 7A9E5B9C8FC324A49A9A0790782C7F4A7F51934777FBCF7E56C66C2A56158680E42419 + 6D2749B7D624FDC9AC2B890BF8594CAE427E79DB4B0551A1512736ACA58CA8ACB41DDB + 854A08B546170190075051E91A85529795044500E239514A2ABAD1BE0D4C9F6512A887 + C30903AABECFDB6A6708185641402473637C80806162BDF85C2133B0BBA05450E63886 + 0784902616621FE7529A107A5A976FAC4985C3583BEFF33DC4A05E52767599D426C11B + BDE0A0C0BC45B6AEC3747A316E3119ADA71A5B6589927797483E3613E9F836C87685A2 + 6BB35C93CCDCC8B2C3B8AF38C60B2B8BB54B21C9ADC91644D541E170AE1A97ABCAC321 + BF41855A48B71CF9250A39948601100AD1B26DA8740E2AA117E60FB813848A126FDB87 + C0F2856B0497104250590B9041919A0253ECA5F283688D25C7788B68A21450F388B1FF + DBB6736A9E38CCBC2A2B0F6A64AD45F882EDAC680AF060C0FA2B73921E7F053EDA5454 + 9176BE0E65ABD94B490FE75681822B21B4784D849FB32C76DF08C817846A2628B11185 + 80818740B637CEF7B31C1EC2B10FA7ADB8B4AEC9B541C568244E74A4D94817E7B294B7 + 8A637A8AAD9F762028398B757063445C1BEE807B316788A3FCB7F8A288C5463975301E + 4EA27171738EA0983D5AE01CD38811E9699F5065101CCCA251C0524CB5B147B98FF659 + 02243C98B1992475D297910383F6AAC31BE8A32D8A6341F436F5C4928826AD4628C51A + 443826C05314E11851BC41CA1571E43061BB730A5AA1CBC99636903745458851E28864 + E54A885C0BC88C423670795777784A9607A3BD2004F1F51871000DC6F2578AC434D486 + 871E665D14C76B1A416CC72676A96C4E6F79B75BE23FA31C2527D7C976133F4B211E07 + E0BF4ED46C2FB77D81272290DB760DE8B567527B36316276AA9A3689AF4B3790C08820 + A844258D139856944EB8C40C9EBB6D199A196D446FCF25B0E616884591950021AC9D5B + 62AFF2A46A955C36D21EC41B294EE3AC6235323BA1B3A3000B384B87813550FCAA470C + 6C6CBCC16D8732C5A3387C16C87B34B60D56B71797B177584C6A20804C9C145698A2AD + A0453ABB290B36124D2690124D1B4A0FB37BAA9A1BA0237690018B66E426B7F1400A8B + 3C0635A79689861F3B1A8041919F600B435179E375859591293E98209A2B07AFE02898 + 2B5532275157E0545F844BC07C0491E55A2757A6DBDBB0A0817D580CA677E8C60823AE + FC76B42845AB54C40BF0D5733198C326AAA99F11667FD583A97195D4091BA790C419BB + 2312684479E15E09D94974B5BF747CCC99BB9F348C411178645555372EE08C514B6AF5 + BC4EAF357FE0132AF374475BFC7754B29945D525E5E45561967002452C9B6A99B73405 + 77D9AC5184911D3BA1E833C3FCD93B3401B3E8913721179D75AAB994A17FA6C9C62C2C + CE4194C563B4A78BABAE8CC82E177835AB351884FA1638898D54912A2E87A5BF14170F + D689E0E494DED76C43EBC23CE564B0EB94A5927DEC720C98346562D375E116AE6E00B5 + 081B69CC45AB8263ADA4304C50615FCB3C78FA37961953560912B01D2C255541B94C92 + 9E1F105B6F462A36436090461473ECAA05BCCCB26536DFC157EA60B37B49B4A17154AE + 6003A1C053E52329DF375F86A48300B7855C6865A7FAAA3F04A10D7821B18A978B8AA8 + DD096C6C101015C4CF385422CD1105E765201A445D9D22B3C61525C4A09C692CA838E0 + 1EEF6B45F82A25B9F15108441A5911C5529498BB629A22543239EA9765453633C51E75 + B8C62F2C2D5E8A524E69097DF8480E8347F0F94ECC193DD9E2C86B7134066189C7FA6B + C12331D5622CCAC42D090553E1191186E6315165BAD5039AA3A75A777AB9057A59D463 + C0E93B5DC34A058519848C78A607ECC631CC050554CEA61B29658A75D4D33DBC95AF5E + 587C1EC3B2B8E717FE85B8D3D41AE5C4700DCC449EA65491B55685791686CB9B51E97B + 18671D92F1A356A3060001CAB220707206B485EA6E0BD85F53719DFA3876C6534058C4 + C291330244C19AC87B944B6945B77B44577772DDC8727C60260C5B17B4819A2C801A9D + 168D0EEA03BCA50236BC20F69886CEFB0C6994BF25C1CC09209EA53B6C04F15F9613C5 + D04646D151457A206F0B35C9A40CC3782B2F851759F93040CC0BC975477C7228BD7D14 + 35DA80607EAB07CAF93F3FAFC623B4458888F05949EA9B2972888FD40F3E6E009F8C93 + 5EFC819BE888E45002962BD84C240119DDC2FB54BAC77FFB2D990600F53E83DA47DE79 + 15EC78E7947BADC403546041E8985FB1BB2CEBE867C9D6E08C213E54992D#))) diff --git a/tests/openpgp/privkeys/8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7.key b/tests/openpgp/privkeys/8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7.key new file mode 100644 index 000000000..e0bc486b2 --- /dev/null +++ b/tests/openpgp/privkeys/8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7.key @@ -0,0 +1,104 @@ +Created: 20240419T125003 +Key: (private-key (kyber768 (p #0DE94F420165B79701FB24A7669190CF2840ED7 + B96E4B088D45A9DB4512117E39B2FB23754175B64471CF95C0480078FD0A8611D19A15 + 9303B26063DEA88752C00200C8192A287C27ED707D6B830006D821F9C26F0501F85651 + 866C0CAEA481C7CE59D1FD04548E15C565C95DBE6BD0C32500AE9BC838050A073005F4 + C76D05B1780669F4C6B24F27769F76926543753CBF9B53441484120BA0FD1214FA4AD2 + 2E56D63BB1D0B004FC155CB5445ADEF258517D393894A4235221359789C06B649FDACB + 8D290B81EF82C7987638F42603C9B85C3613166591D384524485C07FC0C3DA957A2291 + 0743105B13E22BDC9989988192FF537B294D59601B97A21810051D580F1FCA453C240E + D198F6D3A6F185511CA99029859B5C17240EA443792533B6F63750A0336D1A1166FD29 + B7669999A025B938C304080C1D8B784AAE4703AE2459F7815F8EB4083B69B83276D548 + 26E00A690C82709423549634CC02F0429126B5CFFD29946752B122525C6610F4A3914B + AFC5E0CBB16507158FA5AC29463744F166A5C105C646399455682E8114F78208CBCC95 + 1C94B986BA20690710466547C70B35687FC6047F79C78715CB533C31040C8E34C95C4D + B220F10A8EF6CBCA6B0B5B7704339B430C0530DAEB37CD17C96D3821FDD331C001788A + 9068E913B1A697549D1E32C5D72215365CC9B4C59DC8093EE3482AD0C29EF19341214C + 50419408B2403E4830066D1009D55B14D569F65733D0DF5362030A424A5ABCF25C268C + B087591AD8DA4C6B8A80F11D14552671D71183383520152D2C44B3C76B3C875D5FC432 + BB46A7CE5833E91928AD55D30D7789718364BB43900C98FEE5315C8035DEB94387265C + C4EC48927158077125673CBB649E82B890A16E1E5025E598DD544510D5C133F7C0036E + 665AC2A5951C7A75299777EB8A8C44ABCA15339DE0388320067394321ADCC346C49926 + 487CBB2D9B0D130C53B1471D7A939FE1C323A62C1325690A5866702182F98F550AFD0A + 5551A2D01AC7E66038AD4E7682D642D4EA020939A7FC0E30FF228AADFF935F54CB02BB + 03D7C10A001E81ED1151DDBC77E912297D7F922E255A5AEF979172B218D6CB365222ED + CD6852A30ADEF0CAE5C0C7783493E7A9530FFCB7A490A69735ACB86563AF000C89B922 + D39F6C1F638AF1F0BA5E7B11961EB7FE8B631EB38AD60F2C09FACAB473A71AC391527C + 502BEE27B11115F9CA1869F6033A97590D984100E540AB9AC2744F921DE2C3D2A1B9CC + 58231B755C270BC29242C9693493D88701447AC50011374F8E87A1313415E512D77192 + AA2D084F65C6900E72D41D8826B7CB84C26720685C53F5A47FE0220CBE2CA95C827178 + CC677D1098741C767E55BF52B92D5152029EB30D83CC28ADB7E774B06D64A4C0E75424 + 51188B482778864ABEAC0645B07993F98A202642A59940AFE5161C0C535DEE6386AFC3 + AA98769B0D0927FCB85BB7951B5F76354570B24C93A1B277771FAB0C8ECB9326B070C5 + 124C946B3E53A669FC46FEED668CF2955C87974C0429BCCCC6A9C040B8A93CE49E40D4 + C607CB269087742B4D428819E434C41749708154AE8CB5C1C385B60C509008A8B6F15A + B7FC5BD20FB704939BEE164A180BCD2C0A901FA9F30356336576926E24E3516948A722 + 27672594EEA77AEE903#)(s #218B22DB8B1D760A1A070A26950BB1ACE8C5898C2A319 + 583E20A118A56A1E6BB3422B0BB9F466930B50B932780F9E00274590A13B60014E0B22 + D141D074134F577B15409AD58106AD3012C52329DB8E13BBB0B1A753CB79BE9A233557 + 142A60496E0BEAC54334B2394E7CA8B3175956D0CA2CA404FA1930DF36414EB4C776E7 + A041F3409F61384B1AB7B097729473520D18630ED050F0B3957A41C7E34089CB6029AE + E85A25BC3257E7A3EFCA3617D601735BCCD1CB73AF9BB79E90A248A249009D779F9945 + 0E3D82C5D56AEA8707DFF8410835A24DF37B411F3646D6684F11C1765A2C2BFE55F097 + 10467D62EB4AA0F90049AFFF09B1F66560382A7FA5C91CA1C0A696A2FB86CCB8E53480 + 0A4218FB81033E41DA544907A583BE8BC76321AA5D198C80CA295F5E55D98688D35267 + AC38A0822587A825CC90BC721695A9D4414710DFBA3E14332BC5C86A7A9970A624880B + 518749C959384365CA747D2867EC43343C1C61194D5311B555027ABA759F3602E57214 + 7E20C761125D2E86EE1693F80DB809D79138C0AB299712CAFD298209617DAD830EC3B0 + A70649A76F5BDD143476F951767FC1F6F204E434A604C723379837377C8B1A9067E500 + 34E79CC016F30C11FEBA0DB19C2BF42C3D6590C75E75B0271B02DD664402BA75635145 + BE38733C12DE5054759930F36252F5DB2CEF4587965D49B10A4765036C5D5E001700C0 + F9A813559409B72EB4B564C1EF77B28ADA3319DB98EF0183035C1ADF3445C46C320D44 + 31F8C48251893C7CB7421FF65117239547193A4A843019A70BCC42BBACFE9C5BB172DA + CC8AE5D2B15E8338F2EF58FFCA1191CE576A85B8AE5E99231E1156EC9815E499D260C3 + 1AE804FA983C1A996AD3B538386E4680D2067E64234AF0A055E14C818CC2A29524B056 + 03670393BA01642E99BBFA0BCC2681759116C4E1D705D78197EC3D0726DE2A5F9239F0 + 47089684508870B5C06C8715771A1F908923891BEF548194DE22E433C845B48B69D1A5 + A3A289F3FCA2F7032B6176225FA762C7DDCBE1000088D804CEB11BD03A712E7EA2AE96 + 33F9B8765465912E6B4222C64798AC51DE2E09BA4F9A46944256812ACA99A2B08F6AF9 + 205721C227637EC4DE5D4C001D126ED00CB4D8698F1B45E78079187425AF3086F716C3 + 4C72787132C472860AA7B1A547EE65085652106CAC1B48A0057C8CFCEE2C210D90CCCC + 3629D713C142046CA57BFBB738542E360BE6C1DF072CB425199D3915BBDFB0BE435436 + 7F7273ED0471E5129D8C691D7C66EBBE3ADEC86246C45CBC14C474401CEEA3A39AFE14 + 79051A2FE497C4054063508B3F908B00FE797C0A4C60D396B15920537423AECCA2D634 + 09878173D52B66AA4B6C6CA6020ED63BFD47BA85A196E0C66208C4A61E603B94965642 + 884A624309BD21903A0F5693167906DB315FB7B735D290C5A63B58CC8892A4C52C5BC6 + 0F026648B86B6CDE36E7EC68950E224679A2F29FA658B9B3E67560A8E10B5D5A3CB29C + 02743E66FC3E7C90B0973AFB2BCFE935A1277723993924225AC63A60B35314544A9542 + 2D48FFFB3A3927BACB168A72FCABF1664C3E0C91D73A89381AB6BF95A22ADB5C24E971 + 57A1A1D24BC52FD879E0DE94F420165B79701FB24A7669190CF2840ED7B96E4B088D45 + A9DB4512117E39B2FB23754175B64471CF95C0480078FD0A8611D19A159303B26063DE + A88752C00200C8192A287C27ED707D6B830006D821F9C26F0501F85651866C0CAEA481 + C7CE59D1FD04548E15C565C95DBE6BD0C32500AE9BC838050A073005F4C76D05B17806 + 69F4C6B24F27769F76926543753CBF9B53441484120BA0FD1214FA4AD22E56D63BB1D0 + B004FC155CB5445ADEF258517D393894A4235221359789C06B649FDACB8D290B81EF82 + C7987638F42603C9B85C3613166591D384524485C07FC0C3DA957A22910743105B13E2 + 2BDC9989988192FF537B294D59601B97A21810051D580F1FCA453C240ED198F6D3A6F1 + 85511CA99029859B5C17240EA443792533B6F63750A0336D1A1166FD29B7669999A025 + B938C304080C1D8B784AAE4703AE2459F7815F8EB4083B69B83276D54826E00A690C82 + 709423549634CC02F0429126B5CFFD29946752B122525C6610F4A3914BAFC5E0CBB165 + 07158FA5AC29463744F166A5C105C646399455682E8114F78208CBCC951C94B986BA20 + 690710466547C70B35687FC6047F79C78715CB533C31040C8E34C95C4DB220F10A8EF6 + CBCA6B0B5B7704339B430C0530DAEB37CD17C96D3821FDD331C001788A9068E913B1A6 + 97549D1E32C5D72215365CC9B4C59DC8093EE3482AD0C29EF19341214C50419408B240 + 3E4830066D1009D55B14D569F65733D0DF5362030A424A5ABCF25C268CB087591AD8DA + 4C6B8A80F11D14552671D71183383520152D2C44B3C76B3C875D5FC432BB46A7CE5833 + E91928AD55D30D7789718364BB43900C98FEE5315C8035DEB94387265CC4EC48927158 + 077125673CBB649E82B890A16E1E5025E598DD544510D5C133F7C0036E665AC2A5951C + 7A75299777EB8A8C44ABCA15339DE0388320067394321ADCC346C49926487CBB2D9B0D + 130C53B1471D7A939FE1C323A62C1325690A5866702182F98F550AFD0A5551A2D01AC7 + E66038AD4E7682D642D4EA020939A7FC0E30FF228AADFF935F54CB02BB03D7C10A001E + 81ED1151DDBC77E912297D7F922E255A5AEF979172B218D6CB365222EDCD6852A30ADE + F0CAE5C0C7783493E7A9530FFCB7A490A69735ACB86563AF000C89B922D39F6C1F638A + F1F0BA5E7B11961EB7FE8B631EB38AD60F2C09FACAB473A71AC391527C502BEE27B111 + 15F9CA1869F6033A97590D984100E540AB9AC2744F921DE2C3D2A1B9CC58231B755C27 + 0BC29242C9693493D88701447AC50011374F8E87A1313415E512D77192AA2D084F65C6 + 900E72D41D8826B7CB84C26720685C53F5A47FE0220CBE2CA95C827178CC677D109874 + 1C767E55BF52B92D5152029EB30D83CC28ADB7E774B06D64A4C0E7542451188B482778 + 864ABEAC0645B07993F98A202642A59940AFE5161C0C535DEE6386AFC3AA98769B0D09 + 27FCB85BB7951B5F76354570B24C93A1B277771FAB0C8ECB9326B070C5124C946B3E53 + A669FC46FEED668CF2955C87974C0429BCCCC6A9C040B8A93CE49E40D4C607CB269087 + 742B4D428819E434C41749708154AE8CB5C1C385B60C509008A8B6F15AB7FC5BD20FB7 + 04939BEE164A180BCD2C0A901FA9F30356336576926E24E3516948A72227672594EEA7 + 7AEE9037C525E6180138B80E2CA84C2BEEF3319A65284D35117FA4853E2E6DC7F2EADF + D9351CFC435F23FA9D2C91DD9EF3F8ABDB7784586949C248606273835C099D53A#))) diff --git a/tests/openpgp/privkeys/8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859.key b/tests/openpgp/privkeys/8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859.key new file mode 100644 index 000000000..8a047886b --- /dev/null +++ b/tests/openpgp/privkeys/8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859.key @@ -0,0 +1,5 @@ +Created: 20240419T125051 +Key: (private-key (ecc (curve X448)(q #5E42BC08BE62F4D740196FC1888D18F1 + D1BD6D21DAC63C7265BE2897F1C2CE5E9E2140AA3B65620FE0EA2FB443952821AD0596 + 7A6D32D849#)(d #28FCD007DA2A8D37E94628291FD0BDCFE06F07D1586A1FEF914A12 + 092896727F260F49B6A80081A89603F10FF9B40719BF16D20F2E5DA6D2#))) diff --git a/tests/openpgp/privkeys/A1598F57316F7FEC3F946895E35A7D2EAE8D3A13.key b/tests/openpgp/privkeys/A1598F57316F7FEC3F946895E35A7D2EAE8D3A13.key new file mode 100644 index 000000000..6a86fe240 --- /dev/null +++ b/tests/openpgp/privkeys/A1598F57316F7FEC3F946895E35A7D2EAE8D3A13.key @@ -0,0 +1,137 @@ +Created: 20240419T125051 +Key: (private-key (kyber1024 (p #76E799415B04C5B048D8671C59439A7134263C + 18127971232947AA66D66DF13476E0AC5F81736316D38E8FC94E07144B32856A2F7725 + EA1C93A6D4C2668008F77BC41B16B479F313542ACAD6F248A340B9790880B3875D4777 + 6692EA64BC939F880482FE3A981FC88BB9CA36E339BD5175167E73948122AEF0555CFE + 736FA7FC4A36C4634B4502ADB454DA04584C8B1C8F531194A74B15391BBCCA8EDA75BA + C52A75ED11AA3832C7AC00D07A6C6F2238648A7056E3A11BDEA47DFBDB5C6DC55A33E2 + AC5D3826230993BCF960E2D82FAE7273518C1F4B21B8DD3175496987FC1768D8DA9778 + 6C219C054507636901656AE8F62A64B73C179CA5F55C9CD55B54452228408001AADA10 + 6A3C22F320B590C693B1891D82666BA04101810B140B52B20B783C0CA8B875958D75DC + 815805AE85B53501A683E5EB95DB4B61C29ABACF2B31320CB60200648BBA82CCC0A92C + 790583E12CC65643299C544A2219879C512783559851616AD7371C7A21A72BAE6D618A + 65BA32B8C24FEEF25191C5C85E08B4B854AA99A5A386058B9E56C4CB92906EBC811638 + 4ADF96673CC22063462D34D679FDFA632109B9917B2485E8BE80D8025CEA2AA61488B8 + 82B66811B24457C0B11BBD4759625EA290EC67B10C438D5112CDA20993B67C4AE71573 + 235023DC1BAB2D477F7EE882677A3FC7325349854C933AB863E36EF18AAC9A0376B89B + 3764058D7039A8987162C5B2A698B98A4BE4842C065520E253EFDCC0A460BBC1E753B3 + 3A77FE47837CB9B106B5B3737C5D1030CB8F27864E57C54D424A5C69B745E718D2871B + 9A9B7B5C73221FFC8879F4B23211A08A23CE33381EDB972E0BB710B9DC81D6B78BB4EC + 2D856AB14372C32C35BD4188B650F97B954A97736892D2B487F8756821FB308A7B1456 + B1A0C48090DD311AF6AC16FAC8CC4EF4BDFDD47C79E9829ABA0D91F82FE474B58D0049 + A8D15C0ED7ACF154CCBAF398E749B67FFCA176003E72FB00D33CC9E55C18C94C8B7C53 + 48267B9053FA89993AA693DC9291DC9F81E0422FD2BD19029D9BDB88C812A9953C9C9C + B44BA1E6B8470715BE365560B0841428AB11FBA77B9A5A9C442FE35B49481BBEC24657 + F4D20B36B466D1E85C29F2BFD2C784A3C661133C5B8578030D219F6AA9B62E1A6C60C3 + 093B63C8A3484DFFC32847B305F0889411D965EDF132F3B22106276288B44AA91019E6 + C161A472698E380AFC6635497890902CCF937340F73A4A79692700DBC65A4103D47810 + 3C6032668773C91AB63F8729CB897D2B4BAB14A5B6B669BC82162EBB33C752883F14E8 + 2122333B8A6C6C8C4C81610500F5E44F93F389BDD5C5F51AB8A18BCDE75B082E12A58F + 92482FF82E37A26849851C7B933218512794A3649A38B5881035594486B8B7C378D69A + FF18C62C64A27CB740ED1AA84216A796B874DE7CC0D9A4C230A35254829A8745CF523A + 59B5B873B2F03A16CA466876B08781167035863EBC2456C04DC37C8409373CB83ACBDE + DC85B41BADC1831A15A1405D503BE8ECA6B79964A4D186C8D9AC57366BB2A2300CF7A8 + 1B4C8188AB0355E46F28D60416E509F6F034C58056DD27A0A0ABB6A8AB9AEAB8047E57 + 60F7FC3B870A82FF22B408B647A7B497CE555BC632ADDFD1A1E547A06E581C54901695 + 7948F8A745EAC57DBDDAADDA376DD2C643F30366A7F913FFC89A3479C96A4725B8D58F + F4580B3BF5134E027DBA4C6E0F8C5C6E8270D7E1AF10AC7C0EB4A52FB8A0415A9AFE79 + 668F629BB140282781892E88037A84662EEB079A6B1927167DD27909C5188C9AAC1DE3 + 37C555807665C57C8F829DBB3BB74899A4AF831FB6316122989772A57ADE899F990407 + F7FCBC6704670238626A6249AD8665C8AA9302786904EA8ADBD21893246F817AAD0193 + 35E478B257975D39C60DF7AA080F26B37014ABA53C8E4F205BCD049512197200CA5FFF + 8B243E5546FB4A17250376238CB8B3535FB10B773A6CA6179936490ACB8280571A7C35 + 656CA35352279EDA55CD12816BA51C2AA072C2A628AF24CCA8BABD193757DD16615B34 + 09FDF045390C4346792A0D8B2399697BC485B9D8510316C9AF66B6A08E74B1301186E9 + 9ABA3ABB5DB68BC8172C8DF677B78FA60F247B45D3B14D39164592F53DE39AB6B4A96C + 7949964F204C919A576B515F4BF9BB6ABFCE4DD7B5969DB8ECE3DA32CE298B573793F3 + E45248B0CBCFF67D79#)(s #1A8B03F87A2BAACB60D8261B32AC2742BB83BA4014C7E2 + 6847613CF7A7BD44D1C428F4009AE4C42F12CF39E283A5DA2632B649F259ACAD157DFE + 9086B6F0CE919B6490499597AB809EBBA71DD55D6FCC61DF5067C78C8D1907745C703D + 1BE25F0E18057A496FB476C47C8C01CD656619C7213D61223A124B15D38B27F0686907 + 0D0D47476A756FE629C2F516A9F527044BA5931FB8B2EC23A415B9CD510C1233270A7E + C209F245CA82C93B7BF40C2CBB20213A8650EA6934D910B1D80BCCFC9B169704AF7ACA + 2038205CE193C931514865BF5629A901E0BFEFE78FC8657A16999AD4F14AB51648F329 + 08E8FA9A22A35C936888E03581588ABCAF016A1A920BFF394EBD3840EA47731E677E9D + E77CF30701EB805C1A91A5BE531BD400022D19C350E809288A9F6542C5EF391C61C48B + CED9079E691490E92EB60BA7088720873755B997A2A62A1665876CF91B3E4682200993 + 57F94A8B035699D19B64EB480E41074FC011A64508226C7A21B4F2348FE8151A47912B + E96454541CD54A3CE5D74C92643E4210C306F6A10CC890EE5A7575F0853123693211B6 + CE8CB16EB05BDE0933B850270C86B01B026B4F11A6DB12CFE06C79809A8F5357B2E749 + 530D584A76F13E3FEA98003390B56885B43C1BEF82A4E87AB07B19B47689B8D6C4ADAE + 2178886517BCC1C0642A944C426AE6C8540331B73D524DA449BA91F0CBD2558175A9CD + 693B3DC8545D68B97BD3D212D395B1BC4437D40C2AEB5013B829AA40EA7913737730B7 + 252C266E5AB011928BA68D681012BA066257A5DF8C41392970258B9FCDEB133D6C1A71 + DC2C23B52FB6BB8267951C25621ED620CF929517B6F3B233331E80365FAA06978247BC + 01573CFD592F39932B4076BFCFC23F28F2892724761DC02FFD722D795285653AC0B6D0 + 6341B45B980092BAAB270AF4AAAA8AAEFE0896B19B5190A6108205B999199ABFB1BD0C + 15A53CA723BE569BE3912E4C552973A45A356CC88208689C7847D4370456A59D5CB7B6 + 50E8A0C41141C3EC0999E31ED5E7A938C0031A112218E715EA387DA1F327D93B4134F8 + 0C32C960D211CAB173CFF3029E9A10A222985131398C528C6D2B644464590DCAB105B1 + BABAB97C33D2E77C41D417022940A31A72CA30AD9596AB998C5B26152A98685E991738 + E9A9228D1C859C899D76AA1F0C68568C83897751527B462F8842225C118B786CAE710A + 57AFB16577B7BDBBF19CD8B74C035162C5C6967712CF46EC8CE3CC817CBBB5D7B11F6C + 645A2B784A5E226CE2D522236C74CBC5A9CFE26CE4ACB08300826589B8F9B870582390 + 9E2930B5945342DCB592B56D80C13B54C3B1F84BA58E66C102A639E6B1A3C7B4202A6A + 98A1F82668F6C4D61BCF96B3AB8FB132E67A251407207BB467BFB02FD90B04C54809E1 + 12B347BBACFBBC5FA2D16627499AB8C8439D89299EA4C363F46474F8B568511ED3A614 + 797015BC4C25E41B3C3A674CE9A6241FDA07A9027608BB0248FA20671A8E6CF461A989 + 24F314CA7F37C03D397B2E760C9C101717E83064F198ED52098DC7B18D08CD06925C26 + 039536B57E82902CD6E33AEA0A65231A58BBB694FCC76D9985486938BDC8E61244114B + 7CE7C0D466907AF309FFF09A89203755843B4EB89FC7F5C357321BC79B0528941682BC + 2213CC8408552368158C73448EE71C67A40BC6B76251FE860AE08C7922E3CADDBC64F1 + B56000075293970906D848AF430F4B9186A4A50EE2505BEE0084F0A2923FF4A7938009 + 7670B422D2A10D083312329FB24B3F256B37DB05BB10FC464EC1BE48B4A61CE98D8FE7 + 667EFB10CBBB5300F3944CFA403C03CF83023640895A8BD859A4DAA9036450A79775D7 + F83B37CBAAB2454CC3158E8A5152F9E816DACC4693B34FBF238AFA43724637C83EDAAA + 608C36DC15026255CB0B8B4A17284086052C9B449B80D154EDC07A16D3AA0E2B9177F1 + 7D96AA6162D47828400939C22CCB019293528E06E947A8D8C7BB20A0BBA277B45AB677 + 0155CB14C0016B4E64E43F91E8CE660BAECD371512B5A29ED3B759F9C3813314241950 + BF9862079011F6B233C2897C75C63DFFE418759105D4CA1D5470389FC2C0B623412CD4 + A208530A1FDCB743717980926AAA928A55498C1ED4C183F37E26777F405CA949237BD9 + 793A025A8A27773976E799415B04C5B048D8671C59439A7134263C18127971232947AA + 66D66DF13476E0AC5F81736316D38E8FC94E07144B32856A2F7725EA1C93A6D4C26680 + 08F77BC41B16B479F313542ACAD6F248A340B9790880B3875D47776692EA64BC939F88 + 0482FE3A981FC88BB9CA36E339BD5175167E73948122AEF0555CFE736FA7FC4A36C463 + 4B4502ADB454DA04584C8B1C8F531194A74B15391BBCCA8EDA75BAC52A75ED11AA3832 + C7AC00D07A6C6F2238648A7056E3A11BDEA47DFBDB5C6DC55A33E2AC5D3826230993BC + F960E2D82FAE7273518C1F4B21B8DD3175496987FC1768D8DA97786C219C0545076369 + 01656AE8F62A64B73C179CA5F55C9CD55B54452228408001AADA106A3C22F320B590C6 + 93B1891D82666BA04101810B140B52B20B783C0CA8B875958D75DC815805AE85B53501 + A683E5EB95DB4B61C29ABACF2B31320CB60200648BBA82CCC0A92C790583E12CC65643 + 299C544A2219879C512783559851616AD7371C7A21A72BAE6D618A65BA32B8C24FEEF2 + 5191C5C85E08B4B854AA99A5A386058B9E56C4CB92906EBC8116384ADF96673CC22063 + 462D34D679FDFA632109B9917B2485E8BE80D8025CEA2AA61488B882B66811B24457C0 + B11BBD4759625EA290EC67B10C438D5112CDA20993B67C4AE71573235023DC1BAB2D47 + 7F7EE882677A3FC7325349854C933AB863E36EF18AAC9A0376B89B3764058D7039A898 + 7162C5B2A698B98A4BE4842C065520E253EFDCC0A460BBC1E753B33A77FE47837CB9B1 + 06B5B3737C5D1030CB8F27864E57C54D424A5C69B745E718D2871B9A9B7B5C73221FFC + 8879F4B23211A08A23CE33381EDB972E0BB710B9DC81D6B78BB4EC2D856AB14372C32C + 35BD4188B650F97B954A97736892D2B487F8756821FB308A7B1456B1A0C48090DD311A + F6AC16FAC8CC4EF4BDFDD47C79E9829ABA0D91F82FE474B58D0049A8D15C0ED7ACF154 + CCBAF398E749B67FFCA176003E72FB00D33CC9E55C18C94C8B7C5348267B9053FA8999 + 3AA693DC9291DC9F81E0422FD2BD19029D9BDB88C812A9953C9C9CB44BA1E6B8470715 + BE365560B0841428AB11FBA77B9A5A9C442FE35B49481BBEC24657F4D20B36B466D1E8 + 5C29F2BFD2C784A3C661133C5B8578030D219F6AA9B62E1A6C60C3093B63C8A3484DFF + C32847B305F0889411D965EDF132F3B22106276288B44AA91019E6C161A472698E380A + FC6635497890902CCF937340F73A4A79692700DBC65A4103D478103C6032668773C91A + B63F8729CB897D2B4BAB14A5B6B669BC82162EBB33C752883F14E82122333B8A6C6C8C + 4C81610500F5E44F93F389BDD5C5F51AB8A18BCDE75B082E12A58F92482FF82E37A268 + 49851C7B933218512794A3649A38B5881035594486B8B7C378D69AFF18C62C64A27CB7 + 40ED1AA84216A796B874DE7CC0D9A4C230A35254829A8745CF523A59B5B873B2F03A16 + CA466876B08781167035863EBC2456C04DC37C8409373CB83ACBDEDC85B41BADC1831A + 15A1405D503BE8ECA6B79964A4D186C8D9AC57366BB2A2300CF7A81B4C8188AB0355E4 + 6F28D60416E509F6F034C58056DD27A0A0ABB6A8AB9AEAB8047E5760F7FC3B870A82FF + 22B408B647A7B497CE555BC632ADDFD1A1E547A06E581C549016957948F8A745EAC57D + BDDAADDA376DD2C643F30366A7F913FFC89A3479C96A4725B8D58FF4580B3BF5134E02 + 7DBA4C6E0F8C5C6E8270D7E1AF10AC7C0EB4A52FB8A0415A9AFE79668F629BB1402827 + 81892E88037A84662EEB079A6B1927167DD27909C5188C9AAC1DE337C555807665C57C + 8F829DBB3BB74899A4AF831FB6316122989772A57ADE899F990407F7FCBC6704670238 + 626A6249AD8665C8AA9302786904EA8ADBD21893246F817AAD019335E478B257975D39 + C60DF7AA080F26B37014ABA53C8E4F205BCD049512197200CA5FFF8B243E5546FB4A17 + 250376238CB8B3535FB10B773A6CA6179936490ACB8280571A7C35656CA35352279EDA + 55CD12816BA51C2AA072C2A628AF24CCA8BABD193757DD16615B3409FDF045390C4346 + 792A0D8B2399697BC485B9D8510316C9AF66B6A08E74B1301186E99ABA3ABB5DB68BC8 + 172C8DF677B78FA60F247B45D3B14D39164592F53DE39AB6B4A96C7949964F204C919A + 576B515F4BF9BB6ABFCE4DD7B5969DB8ECE3DA32CE298B573793F3E45248B0CBCFF67D + 7994C02B6A67177C09FD8A3F3F2630D907A48ABB59A77AC1F75F344ED2846963561053 + B1E9F15F14583281F621DD991A24EA473FDA7C63FE59C80F5F49708EE520#))) diff --git a/tests/openpgp/privkeys/A1ABFD89944870D04039D40C218EE127254AEEE9.key b/tests/openpgp/privkeys/A1ABFD89944870D04039D40C218EE127254AEEE9.key new file mode 100644 index 000000000..fd50945f6 --- /dev/null +++ b/tests/openpgp/privkeys/A1ABFD89944870D04039D40C218EE127254AEEE9.key @@ -0,0 +1,8 @@ +Created: 20240423T151629 +Key: (private-key (ecc (curve brainpoolP512r1)(q + #04681C6E8D70DEF5DED6097972643ECEA2538EA6CA3F9F87DC1E4B27D37CFDA3296A + F129D5EEB331D836EC7A215CC1CF4103184D21F7AA184C1EE9C338BAB19147438F4C30 + 705E610E142C29F712C913D01132D862F5B65D7FADE1E145B4D9FB08E8B281DA94139D + 11D3FDC0A55B85D1AFF0DDFE052779115A72DE03BB098E03DD#)(d + #32C7C4790D709ADC404D85A791FB119C327FA8E88F835BE8C4076E250ABAD2858237 + 6496ACC61573108A9518789BCF13FA7D33D6D4324D962895F6554DD6A129#))) diff --git a/tests/openpgp/privkeys/A87B85D88DB8B2B5A62A9958C8F2878F49605D09.key b/tests/openpgp/privkeys/A87B85D88DB8B2B5A62A9958C8F2878F49605D09.key new file mode 100644 index 000000000..b3898c99d --- /dev/null +++ b/tests/openpgp/privkeys/A87B85D88DB8B2B5A62A9958C8F2878F49605D09.key @@ -0,0 +1,5 @@ +Created: 20240423T121404 +Key: (private-key (ecc (curve brainpoolP256r1)(q + #0431801CBE11209D65705872CDBED8E8718ADEBC8F4F44D69A71244F883EFFF54654 + 7B31DCC0BC0D1BF5DE953DBE11A753DC3B9BD39DB955DCA30C1F2535F59CB4#)(d + #6418FBDFBCE6B9389971AF84468050995EC79FBCF42BE6AB5A5F96BF4A8000BE#))) diff --git a/tests/openpgp/privkeys/D54E9B75C3541D95C45E430DAC9645E9FB62C668.key b/tests/openpgp/privkeys/D54E9B75C3541D95C45E430DAC9645E9FB62C668.key new file mode 100644 index 000000000..835045265 --- /dev/null +++ b/tests/openpgp/privkeys/D54E9B75C3541D95C45E430DAC9645E9FB62C668.key @@ -0,0 +1,5 @@ +Created: 20240423T121447 +Key: (private-key (ecc (curve brainpoolP256r1)(q + #049A80F4C7499AE14056F51D49D9899D7B73DB1BE7EE62EEEAA477C7A1F96F55F118 + CC0C0F89FF23E636C4F27AC51F6C571802606689A9FD9940D717EEDB0702ED#)(d + #409D4A1B8E6B0C8CB466BCD8D6C0B1D832A73FD8241C6EA65F01EA2D3BFFE1A4#))) diff --git a/tests/openpgp/privkeys/DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93.key b/tests/openpgp/privkeys/DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93.key new file mode 100644 index 000000000..7170d0ff2 --- /dev/null +++ b/tests/openpgp/privkeys/DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93.key @@ -0,0 +1,5 @@ +Created: 20240419T124916 +Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q + #405D1A8C6FB60828BEF043119A1DF97E3A6A408CF3E0CD56F74F9522A7BAACD51C#) + (d #ACF7F220B124A7C726F40DE83D07EFB9165814E9A42EFD9ECB53B27401D85BB3#) + )) diff --git a/tests/openpgp/privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key b/tests/openpgp/privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key new file mode 100644 index 000000000..bc1e024a4 --- /dev/null +++ b/tests/openpgp/privkeys/EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A.key @@ -0,0 +1,104 @@ +Created: 20240423T121447 +Key: (private-key (kyber768 (p #46A3CE8B663A26CB4AFC5A5ED35A405AD3B3A66 + B47A859A458AC6F1BF3518575209D91CD2311777DEA1A14D196E50388AC7A2E2599221 + FF2697F5129FE6A5FC6B89AB1667AAD7179A99111CBD874E3D18699DBC03A601E82E16 + 0547C2AA2962972B5AFD1EA74ECE3C67599693448760CB383FF044F8F716C37493865B + 419872B60173360AC74CCC31035C23A380D7660B536105ABA2CB2598F2434631C733E4 + 952398A182C799C732BE69FBCC94796781AFA137D27080FCE314B9590A8F48261A3FC7 + CB94A2094535E9712A29E02BA50C3BDA4D8B76F68BEE95907BB72A6913829245C7B9EB + C86778994FFC183EB423B9C48AF1A3A149621868CB984A67CAA8A8987DA6983337418E + 50217F0780C595BA39845A724A0400DA35FD0E308D2250F83723B009D41818C38156B9 + 6C5C6ACDBD592BED95D67E939486A16565005EF105D55D7983E534F1E8767814B5C141 + 867A0AC7CBE297CD39885BF837C9602BFD4344158F304B1BBC91F832B291214AAA0BE4 + 7661168F16D9229A1A4B53B0BEC7C657729D0D6365A51C4A7919CC49B7D88DA619A5B4 + 0FB082A33C26BE6100C5D183D2A72634AC46D314825B190B4AAF27573844E0CCCB21C6 + 69513A5279AC0B5F1588D48C01167478E940BC62247A772065E80F339B708651740B8D + B2955F3BA78F50C2E5C645F995BCBCCFB466E0390DF2A595B69193FF3ADF1422D41A08 + 33940355DDC48DD93960B0A4D9681A5BD54234DB94B24C44B2EFC9A90731978B9B4E2D + 12E4C253A58A947651658FA949A267323A79C4875488A7F9C0D22EB743EF6A39EE30E3 + 423B6911677827BC3C2001154027381843E4C58B0B43B59EF317DEC146A27817664990 + 2DC68245406C4835A56BB0174A30C50CD59598F873A979A6F15612B5044798C1AAEEB5 + 40883A33766BB274A1B390BCB8AFC0C9C11A14A4D5568F237A9EC3232AA782BEFE6BBF + 2F8385B5C5E5133232907BAF963789F24B3EE989DC78C3AD0F59045B7ACF66340FDA65 + 5442A19AADA6697175013FB0668C45153B61EF722C2CBF571AE3810801A3B1567CD797 + 49C33955A4B413DCC989F24EA4AAFE80A2C3A24925574E422C11BA8735E9C96E9929DE + 9D263DA7ACD08A80A5BACA156C11161B3A7D0C56BADE20385C51A30941C16D4849EFCC + 89D479ADC6A9C06FC383AD900BA03750EC3BA81F97916D90FBC6582DE92CDF3E7C7DF5 + C9621CA99CCA47D72A171C8C945E6891E421C87CAF523776874A3C441C2A824704ACDC + 5D384F65958D47A46DEB079A1B895F0D37D9CF36F6622A1D19109FB1933B52B4D9D271 + 83496547B1794CDC675437C90C0C36C38F12A23D70B6D83540CC395BBA1B1885A3B8EB + 582D0A5C294E0048E7A94BC1A2E17F89DA710887C3B8241DB4D0891564D939932C047F + B28A2180B878F5BC142EB152E49411C8C1AB33A568E401BB827AD4E7B7B04A89236C40 + 0B233006A89A77CC362F5F63D57715A85EC08BB175348FA305876CD3A282547A3502FD + 14AB85004BEB50EB6C8B2F5E217FF086C86473F1B34672FB9CBFA63BE1DB8A13DC76A9 + CB30C6FEA0A25C828D8A6B6FB725569B888692B46DAEC53332A9AF9B61B1FC92851A02 + 33384A9735B86F076A05F44C97DA491C69AAD8622CEC7530C0BD13CA98B91E58BC1229 + 7CA2C2B37CDCE455EB5#)(s #0679BE74A86EB9B647B637B40EE3A26C4B3EA6D57024B + 56827C02017071E98052BCF0A4CA78036A672849D39975C06173FDAC73E6A732ACCB2E + FF887D77BA3D8E56A34643438A92ABCA1332F8C01AAB537AD721AD8E46DBB6742FE7C6 + 9BDDA9D6FC3139081839AD702979018E41C60BCA2650C682DD003BD05C8AE3622761EB + 775BFCB89EEC67A12211FC67700DD0C62D499C0E75A7084E618F4B141461ACA2387380 + 98947E8EBCDC86440426A8DB0F5737724218E75084E5C0A8CC96BFD842FEE6712B12B9 + A1EF5A506577C7DDC1CD7905F7A853588452CD4A17707546DB6039C90AB82FA17026AD + B035C329AF7309BC560B3706919A2770337E008C14936F30900FCF541FAE7050A49A6B + CA80DDF0695D3A885EBD1AAA80009C9F06D72D10FAD4A1A8908832E151B668575678CC + FB67A4A34B6489CE3CC5865A5AD290853B12668401076A6355837359C27229B748E400 + 2BC24506E42DA05DE5562107138DDBC13B99421C784CACA035766C040C9B8A5925666A + C218E7E1B300D3B5CC0D826F9E36871C860E7497804652F12D65BC2610FD5A3BD6AC4B + B0C38262C719EF66868CD4245F67C623CD5B7FAD28674E21564826A0D5B95E8125B03C + 9C99EB4ADD7B74AC16BC1CCF613453143F935A0AB34222FB6C079A4C372AC1632770FA + 8B30FA9B6399DCBAFA67723C760378DB081FCD8C825B18F45721550FBB86E747C1E7B0 + 2A68A3796266C11FC3444D38CBE0B72D9D885C5C77F0AE48A038B7EE2CA97B1C126333 + A9130DA1BDB94C6317660655C0F09D8C130A72ACA784176D4ADE57161B6801333A11C3 + E119CA2757BBEB08B5112AF06965452C1CD797165E51209BF04C6F90B3373F2CC9E02B + 1A1594D4895B4C18C4774D222C0505BABEA18216642E7B7CBA796ACBC5C82354152ECB + 765B2D56F10E925F57BC45ED1421638AF87B8C125A74473B78D026295F341315D7A1A6 + B1B2EA946817222338E8342C093CD5FC393F1A92931C92678766D2A465E5A4C5A90C35 + 758C20BE0AAB6A8E0752250607DCB0603018CF4F07390AA328CF2C8364AA63FB25C815 + 8A37A69716E743FE47303B498968E095226E59A24E4761676060504C5B0ABB5F0548DB + 3650854FB1AB66061426C3DBB177EFB1C0ADED3C142405C0CF7756A50776EBB76D4882 + 44AD008ABB3766594071C38A835CC477C01A169E0C6AFA43AB81037A6E9606CB234590 + 77DA8CC72F7C98732A56D557201B2C46E29460C37A3BD808C0E7BD346951292BFB51BE + 81C3A384B3277912CCCB521CB0BC81C76565CA65DF354680A1A243915655CA311374C4 + E998977DB488A79B63864C440C8EC0E59F9C169A181AC48CCED41722FBC16D8DC22179 + C3988237EF7E75467DC5AAC434AD6498690F28407A46015491BD0EC779AB079A9C0194 + 5C0B3DF39036F3A0BFE80CB1DF898B24A1354B0BE35560EA0DBB12C30935BF047ACF36 + 1CAC11B8D3C1D1C624B16F76EC3A80783C3742E195B031943C9A9A21702399BF9A4A25 + 848A8951DFB047FCBC4ADBDD79C73EC27F6348281072BD670831BA5B414945CC12423F + 2D5C2CF34C9F4C841AD620B665623CD74CCFC162E0AD04BD0646F581C372EBA355DA8A + 64CA92BE1056B2CFA7B46A3CE8B663A26CB4AFC5A5ED35A405AD3B3A66B47A859A458A + C6F1BF3518575209D91CD2311777DEA1A14D196E50388AC7A2E2599221FF2697F5129F + E6A5FC6B89AB1667AAD7179A99111CBD874E3D18699DBC03A601E82E160547C2AA2962 + 972B5AFD1EA74ECE3C67599693448760CB383FF044F8F716C37493865B419872B60173 + 360AC74CCC31035C23A380D7660B536105ABA2CB2598F2434631C733E4952398A182C7 + 99C732BE69FBCC94796781AFA137D27080FCE314B9590A8F48261A3FC7CB94A2094535 + E9712A29E02BA50C3BDA4D8B76F68BEE95907BB72A6913829245C7B9EBC86778994FFC + 183EB423B9C48AF1A3A149621868CB984A67CAA8A8987DA6983337418E50217F0780C5 + 95BA39845A724A0400DA35FD0E308D2250F83723B009D41818C38156B96C5C6ACDBD59 + 2BED95D67E939486A16565005EF105D55D7983E534F1E8767814B5C141867A0AC7CBE2 + 97CD39885BF837C9602BFD4344158F304B1BBC91F832B291214AAA0BE47661168F16D9 + 229A1A4B53B0BEC7C657729D0D6365A51C4A7919CC49B7D88DA619A5B40FB082A33C26 + BE6100C5D183D2A72634AC46D314825B190B4AAF27573844E0CCCB21C669513A5279AC + 0B5F1588D48C01167478E940BC62247A772065E80F339B708651740B8DB2955F3BA78F + 50C2E5C645F995BCBCCFB466E0390DF2A595B69193FF3ADF1422D41A0833940355DDC4 + 8DD93960B0A4D9681A5BD54234DB94B24C44B2EFC9A90731978B9B4E2D12E4C253A58A + 947651658FA949A267323A79C4875488A7F9C0D22EB743EF6A39EE30E3423B69116778 + 27BC3C2001154027381843E4C58B0B43B59EF317DEC146A278176649902DC68245406C + 4835A56BB0174A30C50CD59598F873A979A6F15612B5044798C1AAEEB540883A33766B + B274A1B390BCB8AFC0C9C11A14A4D5568F237A9EC3232AA782BEFE6BBF2F8385B5C5E5 + 133232907BAF963789F24B3EE989DC78C3AD0F59045B7ACF66340FDA655442A19AADA6 + 697175013FB0668C45153B61EF722C2CBF571AE3810801A3B1567CD79749C33955A4B4 + 13DCC989F24EA4AAFE80A2C3A24925574E422C11BA8735E9C96E9929DE9D263DA7ACD0 + 8A80A5BACA156C11161B3A7D0C56BADE20385C51A30941C16D4849EFCC89D479ADC6A9 + C06FC383AD900BA03750EC3BA81F97916D90FBC6582DE92CDF3E7C7DF5C9621CA99CCA + 47D72A171C8C945E6891E421C87CAF523776874A3C441C2A824704ACDC5D384F65958D + 47A46DEB079A1B895F0D37D9CF36F6622A1D19109FB1933B52B4D9D27183496547B179 + 4CDC675437C90C0C36C38F12A23D70B6D83540CC395BBA1B1885A3B8EB582D0A5C294E + 0048E7A94BC1A2E17F89DA710887C3B8241DB4D0891564D939932C047FB28A2180B878 + F5BC142EB152E49411C8C1AB33A568E401BB827AD4E7B7B04A89236C400B233006A89A + 77CC362F5F63D57715A85EC08BB175348FA305876CD3A282547A3502FD14AB85004BEB + 50EB6C8B2F5E217FF086C86473F1B34672FB9CBFA63BE1DB8A13DC76A9CB30C6FEA0A2 + 5C828D8A6B6FB725569B888692B46DAEC53332A9AF9B61B1FC92851A0233384A9735B8 + 6F076A05F44C97DA491C69AAD8622CEC7530C0BD13CA98B91E58BC12297CA2C2B37CDC + E455EB5F45B962B404EC9D9ADA018C9243AA1C5DB1F39D31391BF62AB800F85D6BAB5C + 503A078F0E1B47A9475FF068E159DED6A0D5C8291D6054524B0A8064D9DA18A39#))) diff --git a/tests/openpgp/privkeys/F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59.key b/tests/openpgp/privkeys/F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59.key new file mode 100644 index 000000000..7f51be489 --- /dev/null +++ b/tests/openpgp/privkeys/F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59.key @@ -0,0 +1,5 @@ +Created: 20240419T125029 +Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q + #40F0C11FA63250E08BD9D0E766417EA5F4E4366B7D60692A442F71E37CFB19EAE4#) + (d #A2319B2E7599FD1AA04578C01EFAECC82B1EDFA8FAB85928244BABB0733653B0#) + )) diff --git a/tests/openpgp/samplekeys/README b/tests/openpgp/samplekeys/README index 682dfc06e..88361ee30 100644 --- a/tests/openpgp/samplekeys/README +++ b/tests/openpgp/samplekeys/README @@ -23,10 +23,15 @@ rsa-primary-auth-only.pub.asc rsa2408 primary only, usage: cert,auth rsa-primary-auth-only.sec.asc Ditto but the secret keyblock. v5-sample-1-pub.asc A version 5 key (ed25519/cert,sign,v5+cv25519/v5) v5-sample-1-sec.asc Ditto, but the secret keyblock (unprotected). +pqc-sample-1.key.asc ky768_cv25519 public key. [*] +pqc-sample-2.key.asc ky1024_cv448 public key. [*] +pqc-sample-3.key.asc ky768_bp256 public key. [*] +pqc-sample-4.key.asc ky1024_bp384 public key. [*] +pqc-sample-5.key.asc ky1024_bp384 public key. [*] Notes: - +- A [*] marks public keys with their private parts in ../privkeys. - pgp-desktop-skr.asc is a secret keyblock without the uid and subkey binding signatures. When exporting a secret key from PGP desktop such a file is created which is then directly followed by a separate diff --git a/tests/openpgp/samplekeys/pqc-sample-1.key.asc b/tests/openpgp/samplekeys/pqc-sample-1.key.asc new file mode 100644 index 000000000..984495514 --- /dev/null +++ b/tests/openpgp/samplekeys/pqc-sample-1.key.asc @@ -0,0 +1,46 @@ +sec ed25519 2024-04-19 [SC] + 652B01FF34D43EA62C5DC897E47CC8B150A7A02E + Keygrip = DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93 +uid [ultimate] pqc-sample-1 +ssb ky768_cv25519 2024-04-19 [E] + CF06288CAAAA850A8B5B2927C8C14C7F0A8906AAEA320DE12A0A15F8E8746216 + Keygrip = 2F4CD0990D56D41A74456668469E3139A7960CD4, + 8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZiJoTBYJKwYBBAHaRw8BAQdAXRqMb7YIKL7wQxGaHfl+OmpAjPPgzVb3T5Ui +p7qs1Ry0DHBxYy1zYW1wbGUtMYiTBBMWCgA7AhsDBQsJCAcCAiICBhUKCQgLAgQW +AgMBAh4HAheAFiEEZSsB/zTUPqYsXciX5HzIsVCnoC4FAmYnnBwACgkQ5HzIsVCn +oC6AIQD/QRW5cK76Fmzo2L/a4BO8a/Ui2Euqp74iOLTYMTa0QikA/jz/8YWk+VJ8 +pFZkuPvkMpYFb8Mo+WtmTpyM7b26zQMEuQTVBWYiaHsdAAAEywMrZW4BB0BkOEEb +Lj6i6ki2gYYMLKU3l4xpByy9BKQAaeEiZg9vNwAABKAN6U9CAWW3lwH7JKdmkZDP +KEDte5bksIjUWp20USEX45svsjdUF1tkRxz5XASAB4/QqGEdGaFZMDsmBj3qiHUs +ACAMgZKih8J+1wfWuDAAbYIfnCbwUB+FZRhmwMrqSBx85Z0f0EVI4VxWXJXb5r0M +MlAK6byDgFCgcwBfTHbQWxeAZp9MayTyd2n3aSZUN1PL+bU0QUhBILoP0SFPpK0i +5W1jux0LAE/BVctURa3vJYUX05OJSkI1IhNZeJwGtkn9rLjSkLge+Cx5h2OPQmA8 +m4XDYTFmWR04RSRIXAf8DD2pV6IpEHQxBbE+Ir3JmJmIGS/1N7KU1ZYBuXohgQBR +1YDx/KRTwkDtGY9tOm8YVRHKmQKYWbXBckDqRDeSUztvY3UKAzbRoRZv0pt2aZma +AluTjDBAgMHYt4Sq5HA64kWfeBX460CDtpuDJ21Ugm4AppDIJwlCNUljTMAvBCkS +a1z/0plGdSsSJSXGYQ9KORS6/F4MuxZQcVj6WsKUY3RPFmpcEFxkY5lFVoLoEU94 +IIy8yVHJS5hrogaQcQRmVHxws1aH/GBH95x4cVy1M8MQQMjjTJXE2yIPEKjvbLym +sLW3cEM5tDDAUw2us3zRfJbTgh/dMxwAF4ipBo6ROxppdUnR4yxdciFTZcybTFnc +gJPuNIKtDCnvGTQSFMUEGUCLJAPkgwBm0QCdVbFNVp9lcz0N9TYgMKQkpavPJcJo +ywh1ka2NpMa4qA8R0UVSZx1xGDODUgFS0sRLPHazyHXV/EMrtGp85YM+kZKK1V0w +13iXGDZLtDkAyY/uUxXIA13rlDhyZcxOxIknFYB3ElZzy7ZJ6CuJChbh5QJeWY3V +RFENXBM/fAA25mWsKllRx6dSmXd+uKjESryhUzneA4gyAGc5QyGtzDRsSZJkh8uy +2bDRMMU7FHHXqTn+HDI6YsEyVpClhmcCGC+Y9VCv0KVVGi0BrH5mA4rU52gtZC1O +oCCTmn/A4w/yKKrf+TX1TLArsD18EKAB6B7RFR3bx36RIpfX+SLiVaWu+XkXKyGN +bLNlIi7c1oUqMK3vDK5cDHeDST56lTD/y3pJCmlzWsuGVjrwAMibki059sH2OK8f +C6XnsRlh63/otjHrOK1g8sCfrKtHOnGsORUnxQK+4nsREV+coYafYDOpdZDZhBAO +VAq5rCdE+SHeLD0qG5zFgjG3VcJwvCkkLJaTST2IcBRHrFABE3T46HoTE0FeUS13 +GSqi0IT2XGkA5y1B2IJrfLhMJnIGhcU/Wkf+AiDL4sqVyCcXjMZ30QmHQcdn5Vv1 +K5LVFSAp6zDYPMKK2353SwbWSkwOdUJFEYi0gneIZKvqwGRbB5k/mKICZCpZlAr+ +UWHAxTXe5jhq/Dqph2mw0JJ/y4W7eVG192NUVwskyTobJ3dx+rDI7LkyawcMUSTJ +RrPlOmafxG/u1mjPKVXIeXTAQpvMzGqcBAuKk85J5A1MYHyyaQh3QrTUKIGeQ0xB +dJcIFUroy1wcOFtgxQkAiotvFat/xb0g+3BJOb7hZKGAvNLAqQH6nzA1YzZXaSbi +TjUWlIpyInZyWU7qd67pA4h4BBgWCgAgFiEEZSsB/zTUPqYsXciX5HzIsVCnoC4F +AmYiaHsCGwwACgkQ5HzIsVCnoC4X2gD+K/GMuumoiRpDxEMHbzUfdc1lPBy55MNl +3CPRl3Xqoy8A+gIt6w5h3qWdP0DrrkdQub3uQ+XGhdp7GQTgkltOzfsC +=Zup3 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/pqc-sample-2.key.asc b/tests/openpgp/samplekeys/pqc-sample-2.key.asc new file mode 100644 index 000000000..d803e0320 --- /dev/null +++ b/tests/openpgp/samplekeys/pqc-sample-2.key.asc @@ -0,0 +1,55 @@ +sec ed25519 2024-04-19 [SC] + 6BF41D6AC221F5223C2DEEE320E629089768AD93 + Keygrip = F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59 +uid [ultimate] pqc-sample-2 +ssb ky1024_cv448 2024-04-19 [E] + 2D0F59CAFD12DBD9DF9EFD602D7E8F8C2121A5D1841174346D6C8FE8F9AA7683 + Keygrip = 8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859, + A1598F57316F7FEC3F946895E35A7D2EAE8D3A13 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZiJolRYJKwYBBAHaRw8BAQdA8MEfpjJQ4IvZ0OdmQX6l9OQ2a31gaSpEL3Hj +fPsZ6uS0DHBxYy1zYW1wbGUtMoiTBBMWCgA7AhsDBQsJCAcCAiICBhUKCQgLAgQW +AgMBAh4HAheAFiEEa/QdasIh9SI8Le7jIOYpCJdorZMFAmYnnDgACgkQIOYpCJdo +rZNYHAD/WZ/YV3lqcLTOs3u6XSskKVlf4jxAI9/tzRfG8M7qXPkBAOHzfq/CWJss +Y7Vzh/ZT7/96gNhj3wzjJyjMZ2pSXlEFuQZsBWYiaKsdAAAGYgMrZW8Bv15CvAi+ +YvTXQBlvwYiNGPHRvW0h2sY8cmW+KJfxws5eniFAqjtlYg/g6i+0Q5UoIa0Flnpt +MthJAAAGIHbnmUFbBMWwSNhnHFlDmnE0JjwYEnlxIylHqmbWbfE0duCsX4FzYxbT +jo/JTgcUSzKFai93Jeock6bUwmaACPd7xBsWtHnzE1QqytbySKNAuXkIgLOHXUd3 +ZpLqZLyTn4gEgv46mB/Ii7nKNuM5vVF1Fn5zlIEirvBVXP5zb6f8SjbEY0tFAq20 +VNoEWEyLHI9TEZSnSxU5G7zKjtp1usUqde0Rqjgyx6wA0HpsbyI4ZIpwVuOhG96k +ffvbXG3FWjPirF04JiMJk7z5YOLYL65yc1GMH0shuN0xdUlph/wXaNjal3hsIZwF +RQdjaQFlauj2KmS3PBecpfVcnNVbVEUiKECAAaraEGo8IvMgtZDGk7GJHYJma6BB +AYELFAtSsgt4PAyouHWVjXXcgVgFroW1NQGmg+XrldtLYcKaus8rMTIMtgIAZIu6 +gszAqSx5BYPhLMZWQymcVEoiGYecUSeDVZhRYWrXNxx6Iacrrm1himW6MrjCT+7y +UZHFyF4ItLhUqpmlo4YFi55WxMuSkG68gRY4St+WZzzCIGNGLTTWef36YyEJuZF7 +JIXovoDYAlzqKqYUiLiCtmgRskRXwLEbvUdZYl6ikOxnsQxDjVESzaIJk7Z8SucV +cyNQI9wbqy1Hf37ogmd6P8cyU0mFTJM6uGPjbvGKrJoDdribN2QFjXA5qJhxYsWy +ppi5ikvkhCwGVSDiU+/cwKRgu8HnU7M6d/5Hg3y5sQa1s3N8XRAwy48nhk5XxU1C +Slxpt0XnGNKHG5qbe1xzIh/8iHn0sjIRoIojzjM4HtuXLgu3ELncgda3i7TsLYVq +sUNywyw1vUGItlD5e5VKl3NoktK0h/h1aCH7MIp7FFaxoMSAkN0xGvasFvrIzE70 +vf3UfHnpgpq6DZH4L+R0tY0ASajRXA7XrPFUzLrzmOdJtn/8oXYAPnL7ANM8yeVc +GMlMi3xTSCZ7kFP6iZk6ppPckpHcn4HgQi/SvRkCnZvbiMgSqZU8nJy0S6HmuEcH +Fb42VWCwhBQoqxH7p3uaWpxEL+NbSUgbvsJGV/TSCza0ZtHoXCnyv9LHhKPGYRM8 +W4V4Aw0hn2qpti4abGDDCTtjyKNITf/DKEezBfCIlBHZZe3xMvOyIQYnYoi0SqkQ +GebBYaRyaY44CvxmNUl4kJAsz5NzQPc6SnlpJwDbxlpBA9R4EDxgMmaHc8katj+H +KcuJfStLqxSltrZpvIIWLrszx1KIPxToISIzO4psbIxMgWEFAPXkT5Pzib3VxfUa +uKGLzedbCC4SpY+SSC/4LjeiaEmFHHuTMhhRJ5SjZJo4tYgQNVlEhri3w3jWmv8Y +xixkony3QO0aqEIWp5a4dN58wNmkwjCjUlSCmodFz1I6WbW4c7LwOhbKRmh2sIeB +FnA1hj68JFbATcN8hAk3PLg6y97chbQbrcGDGhWhQF1QO+jspreZZKTRhsjZrFc2 +a7KiMAz3qBtMgYirA1XkbyjWBBblCfbwNMWAVt0noKCrtqirmuq4BH5XYPf8O4cK +gv8itAi2R6e0l85VW8Yyrd/RoeVHoG5YHFSQFpV5SPinRerFfb3ardo3bdLGQ/MD +Zqf5E//ImjR5yWpHJbjVj/RYCzv1E04CfbpMbg+MXG6CcNfhrxCsfA60pS+4oEFa +mv55Zo9im7FAKCeBiS6IA3qEZi7rB5prGScWfdJ5CcUYjJqsHeM3xVWAdmXFfI+C +nbs7t0iZpK+DH7YxYSKYl3Klet6Jn5kEB/f8vGcEZwI4YmpiSa2GZciqkwJ4aQTq +itvSGJMkb4F6rQGTNeR4sleXXTnGDfeqCA8ms3AUq6U8jk8gW80ElRIZcgDKX/+L +JD5VRvtKFyUDdiOMuLNTX7ELdzpspheZNkkKy4KAVxp8NWVso1NSJ57aVc0SgWul +HCqgcsKmKK8kzKi6vRk3V90WYVs0Cf3wRTkMQ0Z5Kg2LI5lpe8SFudhRAxbJr2a2 +oI50sTARhumaujq7XbaLyBcsjfZ3t4+mDyR7RdOxTTkWRZL1PeOatrSpbHlJlk8g +TJGaV2tRX0v5u2q/zk3XtZaduOzj2jLOKYtXN5Pz5FJIsMvP9n15iHgEGBYKACAW +IQRr9B1qwiH1Ijwt7uMg5ikIl2itkwUCZiJoqwIbDAAKCRAg5ikIl2itk36RAQCk +efX2FiFvTEb/SmxMUxNdBXeWGGeAMH3nURRQ/Dz6wQEAlsPzv1lXH3zNLyuhoJrF +lUaDFf52BzCT6VOhK7yR8Ac= +=MDdc +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/pqc-sample-3.key.asc b/tests/openpgp/samplekeys/pqc-sample-3.key.asc new file mode 100644 index 000000000..78f9a8f75 --- /dev/null +++ b/tests/openpgp/samplekeys/pqc-sample-3.key.asc @@ -0,0 +1,48 @@ +pub brainpoolP256r1 2024-04-23 [SC] + 9F7DCCABC11EFE248F48CECD6F6570B33D05BDF8 + Keygrip = A87B85D88DB8B2B5A62A9958C8F2878F49605D09 +uid pqc-sample-3 +sub ky768_bp256 2024-04-23 [E] + B4707EA9BF0FF29F65190D779BE6064181208C59988A80BCD2B2177A9BBDFE22 + Keygrip = D54E9B75C3541D95C45E430DAC9645E9FB62C668, + EAD718DCE3D2F33A20BFC8BA617844DEF3FFAF3A + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mFMEZiemDBMJKyQDAwIIAQEHAgMEMYAcvhEgnWVwWHLNvtjocYrevI9PRNaacSRP +iD7/9UZUezHcwLwNG/XelT2+EadT3Dub0525VdyjDB8lNfWctLQMcHFjLXNhbXBs +ZS0ziJMEExMIADsCGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AWIQSffcyr +wR7+JI9Izs1vZXCzPQW9+AUCZiemUAAKCRBvZXCzPQW9+K6XAP9Y9gQJG6kAzNKX +BxHST/UwcxvA92UBYeHesTIXpop4kgD/dOS9g2UWNAWPkW0/xiGMSuFBIyJOSjpL +Ny73Fn0lmgG5BPsFZiemNx0AAATxCSskAwMCCAEBBwIDBJqA9MdJmuFAVvUdSdmJ +nXtz2xvn7mLu6qR3x6H5b1XxGMwMD4n/I+Y2xPJ6xR9sVxgCYGaJqf2ZQNcX7tsH +Au0AAASgRqPOi2Y6JstK/Fpe01pAWtOzpmtHqFmkWKxvG/NRhXUgnZHNIxF3feoa +FNGW5QOIrHouJZkiH/Jpf1Ep/mpfxriasWZ6rXF5qZERy9h049GGmdvAOmAeguFg +VHwqopYpcrWv0ep07OPGdZlpNEh2DLOD/wRPj3FsN0k4ZbQZhytgFzNgrHTMwxA1 +wjo4DXZgtTYQWrosslmPJDRjHHM+SVI5ihgseZxzK+afvMlHlnga+hN9JwgPzjFL +lZCo9IJho/x8uUoglFNelxKingK6UMO9pNi3b2i+6VkHu3KmkTgpJFx7nryGd4mU +/8GD60I7nEivGjoUliGGjLmEpnyqiomH2mmDM3QY5QIX8HgMWVujmEWnJKBADaNf +0OMI0iUPg3I7AJ1BgYw4FWuWxcas29WSvtldZ+k5SGoWVlAF7xBdVdeYPlNPHodn +gUtcFBhnoKx8vil805iFv4N8lgK/1DRBWPMEsbvJH4MrKRIUqqC+R2YRaPFtkimh +pLU7C+x8ZXcp0NY2WlHEp5GcxJt9iNphmltA+wgqM8Jr5hAMXRg9KnJjSsRtMUgl +sZC0qvJ1c4RODMyyHGaVE6UnmsC18ViNSMARZ0eOlAvGIkencgZegPM5twhlF0C4 +2ylV87p49QwuXGRfmVvLzPtGbgOQ3ypZW2kZP/Ot8UItQaCDOUA1XdxI3ZOWCwpN +loGlvVQjTblLJMRLLvyakHMZeLm04tEuTCU6WKlHZRZY+pSaJnMjp5xIdUiKf5wN +Iut0PvajnuMONCO2kRZ3gnvDwgARVAJzgYQ+TFiwtDtZ7zF97BRqJ4F2ZJkC3Ggk +VAbEg1pWuwF0owxQzVlZj4c6l5pvFWErUER5jBqu61QIg6M3ZrsnShs5C8uK/Ayc +EaFKTVVo8jep7DIyqngr7+a78vg4W1xeUTMjKQe6+WN4nySz7pidx4w60PWQRbes +9mNA/aZVRCoZqtpmlxdQE/sGaMRRU7Ye9yLCy/VxrjgQgBo7FWfNeXScM5VaS0E9 +zJifJOpKr+gKLDokklV05CLBG6hzXpyW6ZKd6dJj2nrNCKgKW6yhVsERYbOn0MVr +reIDhcUaMJQcFtSEnvzInUea3GqcBvw4OtkAugN1DsO6gfl5FtkPvGWC3pLN8+fH +31yWIcqZzKR9cqFxyMlF5okeQhyHyvUjd2h0o8RBwqgkcErNxdOE9llY1HpG3rB5 +obiV8NN9nPNvZiKh0ZEJ+xkztStNnScYNJZUexeUzcZ1Q3yQwMNsOPEqI9cLbYNU +DMOVu6GxiFo7jrWC0KXClOAEjnqUvBouF/idpxCIfDuCQdtNCJFWTZOZMsBH+yii +GAuHj1vBQusVLklBHIwaszpWjkAbuCetTnt7BKiSNsQAsjMAaomnfMNi9fY9V3Fa +hewIuxdTSPowWHbNOiglR6NQL9FKuFAEvrUOtsiy9eIX/whshkc/GzRnL7nL+mO+ +HbihPcdqnLMMb+oKJcgo2Ka2+3JVabiIaStG2uxTMyqa+bYbH8koUaAjM4Spc1uG +8HagX0TJfaSRxpqthiLOx1MMC9E8qYuR5YvBIpfKLCs3zc5FXrWIeAQYEwgAIBYh +BJ99zKvBHv4kj0jOzW9lcLM9Bb34BQJmJ6Y3AhsMAAoJEG9lcLM9Bb34tPABAIHU +3sAgcUS47Plw8XlrX04941JkVaoE/RAGWm4OHAsbAP4hylj3DC0vrKwUkirkJEkY +x4ISI2U8yiITrTcSWAKs9A== +=6QFw +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/pqc-sample-4.key.asc b/tests/openpgp/samplekeys/pqc-sample-4.key.asc new file mode 100644 index 000000000..e913f78da --- /dev/null +++ b/tests/openpgp/samplekeys/pqc-sample-4.key.asc @@ -0,0 +1,58 @@ +pub brainpoolP384r1 2024-04-23 [SC] + A8237D19988A8255E70D2566EC280D0923FB2DF7 + Keygrip = 702F599E35E6E0BE68E6FDF25D887229D42780F7 +uid pqc-sample-4 +sub ky1024_bp384 2024-04-23 [E] + FBCD76AC9908E094D22CAE2564C9CB50EC69AACF0E3AEC91AA115CE43C71DC81 + Keygrip = 19C87B74004E9839F3D56992B0A9943BF90B56F7, + 7C31A4A632A49C4E8B1C8CBA53976ADFF714510F + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mHMEZiemgxMJKyQDAwIIAQELAwMET5MYqqLjo9KNp29LO2t8yvqbd6Vx6aW//erC +Sg/ZbGuzj3T8+YBpbt1fTLyitiiuJMnbwcYO8dWAnU1UTrqgH3dEIz4kgQbZimfO +HtUtFPqUL2CQyZiKpe6yNo4Z9nnitAxwcWMtc2FtcGxlLTSIswQTEwkAOxYhBKgj +fRmYioJV5w0lZuwoDQkj+y33BQJmJ6aDAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMB +Ah4HAheAAAoJEOwoDQkj+y33WBIBfjsh0jq5dpbhgcJ+CWRSdbFUtLzm68BpuEbS +Xk3Rzfu06NahW7N6My5sXZTtkblgfAF/el7MA1ezHITsTp0xC+qrl4LeU5qq3LjB +AZ4vQZUYiparLFLGjgFydw//j4S/z8MPuQabBWYnprIdAAAGkQkrJAMDAggBAQsD +AwRy+w1aCgHlXCnp+4xcQlvfNxUNr6PFVseG4v754BGRnmg9vHcx0Sgf25eAxLf9 +d4UZhRa+IDPQZEi6fqOcK8txKLweCz+B8uc0Q05v6Wsp4ZxXtCPFAJE0AQzYf63K +Y6EAAAYgmpoHkHgsf0p/UZNHd/vPflbGbCpWFYaA5CQZbSdJt9Yk/cmsK4kL+FlM +rkJ+edtLBVGhUSc2rKWMqKy0HduFSgi1RhcBkAdQUekahVKXlQRFAOI5UUoqutG+ +DUyfZRKoh8MJA6q+z9tqZwgYVkFAJHNjfICAYWK9+FwhM7C7oFRQ5jiGB4SQJhZi +H+dSmhB6WpdvrEmFw1g77/M9xKBeUnZ1mdQmwRu94KDAvEW2rsN0ejFuMRmtpxpb +ZYmSd5dIPjYT6fg2yHaFomuzXJPM3Miyw7ivOMYLK4u1SyHJrckWRNVB4XCuGper +ysMhv0GFWki3HPklCjmUhgEQCtGybah0DiqhF+YPuBOEihJv24fA8oVrBJcQQlBZ +C5BBkZoCU+yl8oNojSXHeItoohRQ84ix/9u2c2qeOMy8KisPamStRfiC7axoCvBg +wPorc5IefwU+2lRUkXa+DmWr2UtJD+dWgYIrIbR4TYSfsyx23wjIF4RqJiixEYWA +gYdAtjfO97McHsKxD6etuLSuybVBxWgkTnSk2UgX57KUt4pjeoqtn3YgKDmLdXBj +RFwb7oB7MWeIo/y3+KKIxUY5dTAeTqJxcXOOoJg9WuAc04gR6WmfUGUQHMyiUcBS +TLWxR7mP9lkCJDyYsZkkddKXkQOD9qrDG+ijLYpjQfQ29cSSiCatRijFGkQ4JsBT +FOEYUbxByhVx5DBhu3MKWqHLyZY2kDdFRYhR4ohk5UqIXAvIjEI2cHlXd3hKlgej +vSAE8fUYcQANxvJXisQ01IaHHmZdFMdrGkFsxyZ2qWxOb3m3W+I/oxwlJ9fJdhM/ +SyEeB+C/TtRsL7d9gScikNt2Dei1Z1J7NjFidqqaNomvSzeQwIggqEQljROYVpRO +uMQMnrttGZoZbURvzyWw5haIRZGVACGsnVtir/KkapVcNtIexBspTuOsYjUyO6Gz +owALOEuHgTVQ/KpHDGxsvMFthzLFozh8Fsh7NLYNVrcXl7F3WExqIIBMnBRWmKKt +oEU6uykLNhJNJpASTRtKD7N7qpoboCN2kAGLZuQmt/FACos8BjWnlomGHzsagEGR +n2ALQ1F543WFlZEpPpggmisHr+AomCtVMidRV+BUX4RLwHwEkeVaJ1em29uwoIF9 +WAymd+jGCCOu/Ha0KEWrVMQL8NVzMZjDJqqpnxFmf9WDqXGV1Akbp5DEGbsjEmhE +eeFeCdlJdLW/dHzMmbufNIxBEXhkVVU3LuCMUUtq9bxOrzV/4BMq83RHW/x3VLKZ +RdUl5eRVYZZwAkUsm2qZtzQFd9msUYSRHTuh6DPD/Nk7NAGz6JE3IReddaq5lKF/ +psnGLCzOQZTFY7Sni6uujMguF3g1qzUYhPoWOImNVJEqLoelvxQXD9aJ4OSU3tds +Q+vCPOVksOuUpZJ97HIMmDRlYtN14RaubgC1CBtpzEWrgmOtpDBMUGFfyzx4+jeW +GVNWCRKwHSwlVUG5TJKeHxBbb0YqNkNgkEYUc+yqBbzMsmU238FX6mCze0m0oXFU +rmADocBT5SMp3zdfhqSDALeFXGhlp/qqPwShDXghsYqXi4qo3QlsbBAQFcTPOFQi +zREF52UgGkRdnSKzxhUlxKCcaSyoOOAe72tF+ColufFRCEQaWRHFUpSYu2KaIlQy +OeqXZUU2M8UedbjGLywtXopSTmkJffhIDoNH8PlOzBk92eLIa3E0BmGJx/prwSMx +1WIsysQtCQVT4RkRhuYxUWW61QOao6dad3q5BXpZ1GPA6Ttdw0oFhRmEjHimB+zG +McwFBVTOphspZYp11NM9vJWvXlh8HsOyuOcX/oW409Qa5cRwDcxEnqZUkbVWhXkW +hsubUel7GGcdkvGjVqMGAAHKsiBwcga0hepuC9hfU3Gd+jh2xlNAWMTCkTMCRMGa +yHuUS2lFt3tEV3dy3chyfGAmDFsXtIGaLIAanRaNDuoDvKUCNrwg9piGzvsMaZS/ +JcHMCSCepTtsBPFflhPF0EZG0VFFeiBvCzXJpAzDeCsvhRdZ+TBAzAvJdUd8cii9 +fRQ12oBgfqsHyvk/P6/GI7RFiIjwWUnqmylyiI/UDz5uAJ+Mk16IlwQYEwkAIBYh +BKgjfRmYioJV5w0lZuwoDQkj+y33BQJmJ6ayAhsMAAoJEOwoDQkj+y331g4BfiAE +9eRbjyVlWhuHPFETqMyGnkaB8G2OmY96TMCORisMyhfk3ahwJ8BNS3XLI5+tnwF4 +iMv+X1/eMgtVY8DCDox4fw6kbDNvR+rR1CfkBMn/ewKKc0IZ5BuQE+ByzMw1ujk= +=Ztpi +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/pqc-sample-5.key.asc b/tests/openpgp/samplekeys/pqc-sample-5.key.asc new file mode 100644 index 000000000..6da585015 --- /dev/null +++ b/tests/openpgp/samplekeys/pqc-sample-5.key.asc @@ -0,0 +1,61 @@ +pub brainpoolP512r1 2024-04-23 [SC] + 7B3986A550E5DB116054B4B42CBE157D37FDEC1D + Keygrip = A1ABFD89944870D04039D40C218EE127254AEEE9 +uid pqc-sample-5 +sub ky1024_bp512 2024-04-23 [E] + CA44B5ED43D33290398C5D0983EE5EE4721EC5C680AEA12C2282451D3ED65F4F + Keygrip = 513906BEA5A40F25C9D6EBBCEF62D0784E7235A5, + 6EC551A7895031EE4543A1C789E16E6A6C229CFC + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mJMEZifQzRMJKyQDAwIIAQENBAMEaBxujXDe9d7WCXlyZD7OolOOpso/n4fcHksn +03z9oylq8SnV7rMx2DbseiFcwc9BAxhNIfeqGEwe6cM4urGRR0OPTDBwXmEOFCwp +9xLJE9ARMthi9bZdf63h4UW02fsI6LKB2pQTnRHT/cClW4XRr/Dd/gUneRFact4D +uwmOA920DHBxYy1zYW1wbGUtNYjTBBMTCgA7FiEEezmGpVDl2xFgVLS0LL4VfTf9 +7B0FAmYn0M0CGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQLL4VfTf9 +7B3UcwH+JBipc+OsxxqdhGeoNrYUAmDL4NoXMAhkTYPa7Qwub48ThWz3GjFOCnqr +dyfxBPrEjW5MjRarC2eomaWO7mGbbQH/VXLH4/pq2MGlrhpRfupidbWDmFtHH2NQ +oXSACpYNSTAjuy72p9s4dYbMMWRl85qQxh+4PtmY5XgQXOfd0FFgF7kGuwVmJ9Dq +HQAABrEJKyQDAwIIAQENBAMEbiaJbqyNPX8x8LV0Of/bwAeIQc73qamKsV9In+40 +6dFeIFDq/KDNTHAh5eAY9gHux+3eGqzpWeoT+EFDhhSJ3VStqk/4bl/3XjzC6mRT +cWB13ZCLlke0UlemSuiN05DXMlueMGmAJxazdDr+en5ESVr2JcPgCcWBxj40Ghai +PQcAAAYgvndoszgAtYOXhBSWXsQ50ppBgGVbCli6QMucKdEAiKB9ZrR65hJLmGFW +qTR4ePvPwDUQSdLBiDXO5yut4ot6yLp/+DRa2IWx5zBAmTnFs6Glhnq3kSU48jRZ +qjNSBOzF6UqbZRdyqaxcYKUTeqdHGZlkedekJIORcjmAeFdJOCWJbGKNoXJkGNhD +5JRvmDii4tpbk8aAWZXNfUsm63m8vWReUrMXA5hmhssqsmoiAph5vVchKSgJbwlU +plZU+tZlH+sRA5xuDwMXICUwNICub1oO14ahfjd5q9d1sVQHtlYMl8Qs+Gpj16oQ +MnZ9INl+rXo+/NNG0AtN/RR/rpx7zGQiSDe2xidwduJb51bCAaWERXea6YZ5x1PC +u6aUJdNDpRVurtF6rPC5OcSbP+g6qAxeQ5m+gcE/Ciy05rCOWIoTuJRKyfdNeVQf +qxaRvogVb5IPqDbE1BrEllAJ+qF02ja1Uhx59DE7SYUlMRNpJhtkSPx67mudREVb +9jR8NfK5n5UlTGAndZuZyhZlkAa60ffAsgy9v/Zqj0VOJIY7F1oLYUgwrIrLGvuT +8dPFT3IW2qBa4hC1e1A+Rle2hwqM1EhkS3pbaPVl6fUcnxKJCsgxXtUgFfa77YnG +kkvHVut1L7NQK8UMr6Bz4bauPdKeDJJp+LUFygZUcCEU8DuugclFsTYx6mwoskS8 +37FZtVserKwwr3hyvTyPW+oO6QVtsubIfJsgSrIQGGliF9ZI78u7TAeMFnUbA3Ar +r0R/+3Qf2yRVr1qbymozQMW49rgnSHIwJLIOb1o9VieNvJddTqcNk5mm41VTOSCj +xehYrVGAvUQ08+QrlxkIqlkau/PB6/JXYnyyv3QNwbiMdVGluEYvbOO1jGWjkMB8 +AaFcK8YkqwB5JNm3bdGlBgPBkieKJMyGz6qVLmas/pmoANVsRzp4d5Vfx9g8HdVI +pXUnslkLVrbLPoUl8GmGclN5xBMPj+FVJNVpNGqvRRU43GYxSfGUbVUJvju2AmAx ++HrEshuA1mmB+FJ82vCtS/wqOzJlvaxOZRkLXXdydYluojhgbKeyFzwcTeAQWJZR +KLyGa4zCmRBFrkcyQkPLsICOL4UfyVY3eawmlmSwGuy+9FRl/rlYTMR/OwMXlFlL +9XSXRGsKhWdVQppziaIas1c8LYoDYjhIZFEBPWR9KIl5IAqDcsYDn6I+NYdwCECM +fIFeVQNaW6kWW7s7pWqWJTtSbdw3/MKfFvGOfWZ2o5O4Z4SchZJ8r5lqn+OI/4gN +VhWsZLe0JRgD15WZ0iMtVgyF9WJkPpufM/gTnQthq+mPdgQ+UWYUWgRbg+V0qrNd +nRGaPrMQf7M4jvSDsFZyazA2wpZjAYySn9ly1ETFQ3MW10mvDRJVbxWfwCWExrhF +a/VtwhKNymG3lOeq+2GWk1S1ptqC61Rc+iQs6ZelFoqkOSYTEWW1NPu+HxWk1EMv +9buERqV15ShIC3w/oDKMgTTAv3eHksBZAusyR2MmrpeczwyAw5xLczKfxOo2UWSo +SNUpvLQ5hdNbOuMLAqwFfhkQvbYW9Qp58iRBs1HHfIoXq0BS9EkwuZGvfHUSCSU8 +TSfIxRZsMoWhsdOZgbhezlzHrxG0uLOBcJY3ZuYTCmar1zZHgGfAqGRwxUIhauFZ +NZFi9yw0JBYB5PVAh9ZKh1LDj9hcwJwMfzjOOgMg5eFrUHMC14MOoYeTd0o9e6qA +eXGtFzMtiGLA+nV9mHgs6SdXejK0W/GO7nezk/QQYgwDCTYgRwu8kzE0ifEclocc +QtopaQmr8WqIWVCiz4ls0Yph1Us8UpyWNVrC08aTJMOuY0yHGMPL+nNMQOePkVOh +KGs3ngt2NhQd0gtDomtrsuA6VvAI8IaM4rlAzxCPznlUwKG5M8wV/hVuLExOTty7 +oehxCOPP8khlVIqDjnYZMVslwmU0ZRWMrkaDqgNpaHDDlzWbEYyzeqhT1GzFsFrP +28AQyJhChAcAk0CkeToFcYsOEulfqGhaITuFWbRAIbjDpFivgcJHZ3Wr8AFdv/gg +QnyjBiZd6a4PSuR032J9zRZ66p+3pus/Ahukp4rhESdfE6ySPiOIuAQYEwoAIBYh +BHs5hqVQ5dsRYFS0tCy+FX03/ewdBQJmJ9DqAhsMAAoJECy+FX03/ewdgGQB/3hG +AN5CHePZ4HY9ijl6NtptnzotFl0upj+ItZGsQAd+VroLkPI3Mk0HBecsWplYniS5 +ccBhUFTxlrDpPUXziHgB/0OtCd8cWWJtDOKs2rqdGxb+wfGKYmpOfELMIM6xkh43 +xYAjT76qD4Ht7Hf8tXBLJ3sYfcR2CCSQlDmoXrEUdFg= +=qQEJ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplemsgs/pqc-sample-1.enc.asc b/tests/openpgp/samplemsgs/pqc-sample-1.enc.asc new file mode 100644 index 000000000..c07b46fb4 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-sample-1.enc.asc @@ -0,0 +1,81 @@ +-----BEGIN PGP MESSAGE----- + +hQSaA88GKIyqqoUKHQEA0pRJ6Vj5C0XlgpwLd18OLgMMQJ9nw68AA2f/tCrZBVcA +AARAdHv1SfxOewaAWAp79c8vesQ1ToGOeM/2l8LXhbCB67f5RFkdJVl9yY1WyTpz +IHnlynbB53sNvoS2re+fKySS7fj9nE6n7+RF+NNkS3z7aSPUaJ71Yk3tYZDP+mLd +45g4eqj8O7hpwgS0bCq4a35Oq41dNS7g2S9psgdlC7BGRnzVwPh1v5jF7PTb9WdQ +2GCmvR4Mo87McT5YLv0GNg51aYbx6iD2clcuhBEKx85E+DMnU7+BWqC/Ly+It8d8 +/gPQha7y4ZMkfNlaHSJtcmOqS9gDek+auLYFUqfrvxo8Kmza7bdsRiN8OeP+o/06 +tPNnRkeOp94jbwUjoIbTlZn55qYD0w5FX7yfZPgfUYqEHT+R5zYU1ow3hmt+18zz +L0bpk+GsCEfSyNHlx9R7W5MTvhjP32Xki/UZKy/4WcqiqZQbqJ7KxpE9ATl9XE+/ +5BzmMbVzdaeOgulOJiLxsvZxFhXTVWYJJMtYExzC1jRw4sic8t/PWy1sPE6un9PZ +oBXOMSnbF2sVFueuFGH7thUI2ZI4oUBV9fM8SDUgHuw6oC3VCOtBuCJ5r6uA7ZaP +i8U0CULEHX0sS8SHoL+QIdfG6MHM0w9zNk1d6LhVvlwyGuyHuBDjNJRoJz3URnIc +UvUC2s60tytiuZLLuMZlUooxcDC+Fp5k2ejNP5AWfT1LQUMdV8vRGKiWaL1PbY1M +96PqA3NDZyd9MJn3o74MEA19HPRNTInTW0xwWejZty6hTLvrBMpZSaTxFwmUuUdb +81LES4AkeWH4SJxS8xse2dlD2JK2tYYZPGgkgj5BIAT3J4/Cy9hgdls5NFPZDNUe +cXxz1LRPvewWxjmv7X5H6niBeHCOHXYXYbUTHw8/acDt/HEjYo6yBu3C7MXn5Ga6 +FvC9XWZAQTiicU6cF7U6/TQWQqZWhrdPyWD9/x3iUr4mJsOl3RBBT3Koz7Tyo41H +RqweL+sCYf1tmgIUsLRVZBVOR1JNRkuW4PL0jBR/rsvDWBQShjFvWb8DiKXhm7pK +1DSpMhiuV2BQuVnMls3aw40V9TPaq/dxqj9U/LIRW2vR9sza3ulS7UWL0ZbgLMjz +hq0nU1XRqstTExGwlokt8TJGfsX15yIllLt6iajXD5kiTzhS2iuuXYq4Fx/LjRoQ +W1VglExBdfy0hjFWQMoKZ0f6SjfoIdqKDZDZ1ygKB4UJW0UE0Wmhq5imY1ih/Jb3 +CR5ksXqp4QDr1oCOo4ZtDANGDOyAWAGxsBW0tRMTqcS1J5LT8RKf6wsti14EFOtp +d/fNUvFz5RMYKdDkRb4V14iwgCsGCpShOyDorN+g4Lya3gEIKfG4Qlkg+J6N426L +jwE1Jxx4S+sMzFii7bUQr+Pu9efh95V2Vg6JbccurqCKsOF2ksqE+1AsZiLYFqIL +Qvj7OTbHwvKmNMBfWmCNOew/ggNeYXrXK5X4q9qQuHgFNrwJKNaSc4qft/0JJ0yF +wBctVCPMUvkdYeCCAvSTAhek9bpOiYJgrIl6Fa3UcwEJAhCQ0BoXGut4xmSZnufk +LZNiq7jAIbyXKcGjfUYJkv4ryEYfk+9fMM6HHXnz5wHKLb5xQMQCioJxQuZIY8ku +p2ioOSIHLnVQg9Q9KKdt4RNpvV59Ca+PDBUXGH8167Fp+oKkix7etYsMEamXnlPg +Hto= +=/vwC +-----END PGP MESSAGE----- + +Created with key: + +sec ed25519 2024-04-19 [SC] [expires: 2027-04-19] + 652B01FF34D43EA62C5DC897E47CC8B150A7A02E + Keygrip = DC60E0AE48E0F14E8FD7C9C36E18C6651E99BA93 +uid [ultimate] pqc-sample-1 +ssb ky768_cv25519 2024-04-19 [E] + CF06288CAAAA850A8B5B2927C8C14C7F0A8906AAEA320DE12A0A15F8E8746216 + Keygrip = 2F4CD0990D56D41A74456668469E3139A7960CD4,8B2E1355C97C34E0AC1CBC9DFDF2526BFE8990A7 + +Encryption with --debug=crypto diagnostics: + +DEK is: 85ae7493cfbafdd7282332dbf539ae7c54257c86de776fa29466378e92a110b2 +encode_session_key: encoding 32 byte DEK +encode_session_key: [32] 85 ae 74 ... a1 10 b2 +ECC curve: 1.3.101.110 +ECC pubkey: 6438411b2e3ea2ea48b681860c2ca537978c69072cbd04a40069e122660f6f37 +ecc_mul_point info: Montgomery/Standard +ecc_mul_point name: Curve25519 +ecc_mul_point p:+7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed +ecc_mul_point a:+01db41 +ecc_mul_point b:+01 +ecc_mul_point g.X:+09 +ecc_mul_point g.Y:+5f51e65e475f794b1fe122d388b72eb36dc2b28192839e4dd6163a5d81312c14 +ecc_mul_point g.Z:+01 +ecc_mul_point n:+1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed +ecc_mul_point h:+08 +ecc_mul_point info: Montgomery/Standard +ecc_mul_point name: Curve25519 +ecc_mul_point p:+7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed +ecc_mul_point a:+01db41 +ecc_mul_point b:+01 +ecc_mul_point g.X:+09 +ecc_mul_point g.Y:+5f51e65e475f794b1fe122d388b72eb36dc2b28192839e4dd6163a5d81312c14 +ecc_mul_point g.Z:+01 +ecc_mul_point n:+1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed +ecc_mul_point h:+08 +ECC ephem: d29449e958f90b45e5829c0b775f0e2e030c409f67c3af000367ffb42ad90557 +ECC ecdh: 2444b6e1183eae269c6d4d9f28cd6de1cfedf6bed90ce6dd7ea732d9503cb32b +ECC shared: 6648e8f006898d6407cc253f6a6c47b638f5ff00c5994580b6272611a8d9ad9e +Kyber pubkey: 0de94f420165b79701fb24a7669190cf2840ed7b96e4b088d45a9db4512117e3 … +Kyber ephem: 747bf549fc4e7b0680580a7bf5cf2f7ac4354e818e78cff697c2d785b081ebb7 … +Kyber shared: 0495e20570cd29d82a4933b8a133c09c39ec2a4e67aa6119cf7eccf2bf74ed40 +KEK: 4aee18ac1e35f67b8e3f5109695831fa77b426d85ed11b74390faed49f65da9e +seskey: 85ae7493cfbafdd7282332dbf539ae7c54257c86de776fa29466378e92a110b2 +enc_seskey: 28d692738a9fb7fd09274c85c0172d5423cc52f91d61e08202f4930217a4f5ba \ + 4e898260ac897a15ad +Kyber/AES256.OCB encrypted for: "CF06288CAAAA850A pqc-sample-1" diff --git a/tests/openpgp/samplemsgs/pqc-sample-2.enc.asc b/tests/openpgp/samplemsgs/pqc-sample-2.enc.asc new file mode 100644 index 000000000..c4233ab45 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-sample-2.enc.asc @@ -0,0 +1,102 @@ +-----BEGIN PGP MESSAGE----- + +hQaSAy0PWcr9EtvZHQG9H3dPoDLcLQQVANcEAM6incJG2J35/unGxZiZbvEcAAXz +W31sr7tX24uIsc1x5YGx8cg4y1AHv1EAAAYgvys33VElGIEJLH3p7mfmneCLh/j2 +/KgS2tjUAAUXT1XODNVxfrUHS0b+vPU49bkhH3+qXv28HW/RCtFqI3jIlOxRX3s7 +vROEjDU63STly9zRMKs5hF/dOKQeXb93A4efSV18osEZsgatdNizoGaUUQs+KU1S +gKpfLJYSfAyTRkveDZrYVY4yW572h/whaYRCYhV/YEnGEBsRKPBj/MqJ0+VVxp39 +jmQlBnRAazZornih1yIuEyoFUGiqVjGvZOF1o1EUHT8gOlSOIGpbvT06G7MJP2yC +t9gO8VTSz2G/uhyL8atc5v37OOSDGI+4MFw4Fejy+taZIIqIUEAED+MWvNnqv9N1 +DtLk3FVgf0PvxLHFx2w/ERjJr80KZ2HtLSgmpC2Gu5qOHgKrPN+a/xb1Gw53TYfJ +9lmj1uNmpArvCRPnuT5g5Xa6mT6mVCd6XokYkJSwwYVyDcVxGIUlfey7vUXdH0Zg +OAb0lFi6phgclktzWbRMAV+oUwASQ11I3Pk5JLEXoFJS6yKHPcLIbEasQ5uaps8i +ZEyuO/vw8lCVdt8MVwwAjR5VilBnR9zZZRP8XkOAHTXk3XQpXhLvJVCwM5371c/o +XOEXR+k4kM1sd5fwahiH/tKIiSxhrgwBbgAlgCGNW1Oc/5uvwyZT/vv4sAi850wY ++Hg0dNSeIB1X8qo3sbj1wQxe3bF5FkW7y4OZZsyp2n57wra1Le5394DI+OxyOx4b +MggevEDSkdntIfQXlxfB4xKJWqB84vtChFrrGIKXN3W5hnwAoOTl318VtGz7Xauk +byPOj4MB+Ki2Q6K3XPZQVVWetiL5vaFzSeeC4Ek8TxZSQZ4EVgRxc/tNqgqoGepI +xb0dtMZv8bgA6I47pFutFItkC9R/d392vDmR4D7Ln9cixhphxoiT/PhSwQlbX2bx +UYybfi0YVw6gdEzOeVI0GQ/Z1gXD18JFHmMfBEsEI5fh0iNlVzzd12eIagl5uTo1 +lJ+EG7Szqwc5tSRdNzhZcFciGmlkfaOVRi3UdWXoNGd4H3X/8qyz+J3z9ErUXTu0 +yqCNP1rIGWZ4Fz3diMbT3uuruwQl5vy3hidKPAp1U1KHjrinQ7gHi9wOTOkyJ5u7 +KGpH+H37gIiejwNdHrQiyt30awaQ3nO8z42NVg74YJ+jZWw4iGzn8rZBHOmvqIMz +jrZN3SmXM9wSctKdDocMgRqwkQUe3fiEW9tZiR8tBK8EgHk7pyYNwbyKQY2qMmIP +OJUMu/5nSSQn9ik2LuIhgMi08pgQ8J9JejnqVl1qNheT59uTtHglvDCiiu+DYvtr +GZMn7UrG3XuTdN5TD+tukGLZ9bxsLTUq4f3QfqVQADz5AsIJeoNti2tH2AErGv0g ++DeDGrCnoHVZQTYoJ/e7OYCCLKaUtsFTiVVBMkwlSV1efQpt0FcYy0DwkdiKxHHf +CIiB4EAeBsN3l+EiWrwkyAD5FN+XR2rycEAEjL8fxoTQhQ+cnL72n92ukDCwXXT5 +36fnyXPuGEwaEES8Q70rXBfvmYHtysTAY+rDL7BRpZ1wMokrHe2N6MLnf5V/AAbF +oCqxJaexmycHLCJsYTDnyvDg9x+f4k8RgSMpEqFytf2ToSaemwJsfaU9SDO2dgWD +RROUaM4PzCzDXst/UgIbCvs30XWGuhAM2EGygciW5n7ZuM/wmcKlmJ4gs7Koi2mJ +3v3ZO5xN1yx/RVkR1EQTK50Q4u7Kcvf1hvghHZfqIM+jmzpOIG2b8Bos4kzPWxYL +aGdWnbgdxPlA6p9+Vmg30ykrjFKfuERg3RPPngB5a3MlNExQrOUQll+y9WVKRrzh +ZRay0PTba7moG2SOeS5tKyagItWkT85EMGcKNWSTFbQueps9kSXpEWdBKb3aueAi +5jjR9ITM1iGbo2JITTcNCIDG5sCOAQKNT+GjjP540bQge51KmeZE1smF8tSVQG/2 +kbiQrikcOGmws4aD2xbTkf/NoiW45OXdHjd+0zNOo8vKyUVywObOz3m8mq0LOObK +NlK7BGIEMNWB95OMCQkT8o9uuGDE+N4L8mUenHTn0rxQ0du6GYUhqgVCk9yfbDKF +WxtiMb3sAi9F5VUJKAk8KnUkX7JOFuKuS84i2lh3dxNqTNeU9mT7fTXUXnrmzTxa +gx74k3fUrQEJAhANgfbbcB8gLTkXAdHgU1FK0gjUeKaOVsKVIVonkO2lZL6fn0rz +WQecKUE5mDP4oj9B85RHsxxMCotW+GhUakgMh/Lprwr6uRDPi4vaczKZU83Os69f +YkXcdiJL1B5R6oEArn/p0Ig+vuiBIwQyzXK3lP6oCX8deMp8e03EuI4s1UD5iPsB +xSK9Foaw8uxlKMlgfdgD2eX/UGpVnvbFv/oEdHHACy5hiL56 +=fcr5 +-----END PGP MESSAGE----- + +Created with key: + +sec ed25519 2024-04-19 [SC] [expires: 2027-04-19] + 6BF41D6AC221F5223C2DEEE320E629089768AD93 + Keygrip = F5DB116462B7BD2FA83A4453C4DFA2AE8604FB59 +uid [ultimate] pqc-sample-2 +ssb ky1024_cv448 2024-04-19 [E] + 2D0F59CAFD12DBD9DF9EFD602D7E8F8C2121A5D1841174346D6C8FE8F9AA7683 + Keygrip = 8F9ABF3E5BBFC50D168DD524EB8F7263E7B33859,A1598F57316F7FEC3F946895E35A7D2EAE8D3A13 + +Encryption with --debug=crypto diagnostics: + +DEK is: 244ab936bd8c02d89486abe92a5c145fb502099445987ed21ca78fa42e7f45ea +encode_session_key: encoding 32 byte DEK +encode_session_key: [32] 24 4a b9 ... 7f 45 ea +ECC curve: 1.3.101.111 +ECC pubkey: 5e42bc08be62f4d740196fc1888d18f1d1bd6d21dac63c7265be2897f1c2ce5e \ + 9e2140aa3b65620fe0ea2fb443952821ad05967a6d32d849 +ecc_mul_point info: Montgomery/SafeCurve +ecc_mul_point name: X448 +ecc_mul_point p:+fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff \ + ffffffffffffffffffffffffffffffffffffffffffffffff +ecc_mul_point a:+98a9 +ecc_mul_point b:+01 +ecc_mul_point g.X:+05 +ecc_mul_point g.Y:+7d235d1295f5b1f66c98ab6e58326fcecbae5d34f55545d060f75dc28df3f6ed \ + b8027e2346430d211312c4b150677af76fd7223d457b5b1a +ecc_mul_point g.Z:+01 +ecc_mul_point n:+3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9 \ + c44edb49aed63690216cc2728dc58f552378c292ab5844f3 +ecc_mul_point h:+04 +ecc_mul_point info: Montgomery/SafeCurve +ecc_mul_point name: X448 +ecc_mul_point p:+fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff \ + ffffffffffffffffffffffffffffffffffffffffffffffff +ecc_mul_point a:+98a9 +ecc_mul_point b:+01 +ecc_mul_point g.X:+05 +ecc_mul_point g.Y:+7d235d1295f5b1f66c98ab6e58326fcecbae5d34f55545d060f75dc28df3f6ed \ + b8027e2346430d211312c4b150677af76fd7223d457b5b1a +ecc_mul_point g.Z:+01 +ecc_mul_point n:+3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9 \ + c44edb49aed63690216cc2728dc58f552378c292ab5844f3 +ecc_mul_point h:+04 +ECC ephem: 1f774fa032dc2d041500d70400cea29dc246d89df9fee9c6c598996ef11c0005 \ + f35b7d6cafbb57db8b88b1cd71e581b1f1c838cb5007bf51 +ECC ecdh: b8993bc015a983286618ff6ce866ef846d4cca616934afd8f1ffef9d25046620 \ + 81bb9d8316a3ae5199720f27dcf4ac72fa9e1d71db9b1745 +ECC shared: 634b233b4674df04158bf3b3eb287b50e10819aabaaaa9750e1c603c40b244e9 \ + a0dd353e8098adbacd277f9d79bf2bdfb4ba4401d71b871272d20c99ee3b02c1 +Kyber pubkey: 76e799415b04c5b048d8671c59439a7134263c18127971232947aa66d66df134 … +Kyber ephem: bf2b37dd51251881092c7de9ee67e69de08b87f8f6fca812dad8d40005174f55 … +Kyber shared: 8fd48c5489254e89bcea602904323a370050eee86240e171181e5d1602288aac +KEK: 8595d1368fa33a7ba26810adfc1f66a688768e07c6610766d80d0dcffe1ff3c1 +seskey: 244ab936bd8c02d89486abe92a5c145fb502099445987ed21ca78fa42e7f45ea +enc_seskey: 28093c2a75245fb24e16e2ae4bce22da587777136a4cd794f664fb7d35d45e7a \ + e6cd3c5a831ef89377 +Kyber/AES256.OCB encrypted for: "2D0F59CAFD12DBD9 pqc-sample-2" diff --git a/tests/openpgp/samplemsgs/pqc-sample-3.enc.asc b/tests/openpgp/samplemsgs/pqc-sample-3.enc.asc new file mode 100644 index 000000000..6ceed5f6b --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-sample-3.enc.asc @@ -0,0 +1,32 @@ +-----BEGIN PGP MESSAGE----- + +hQS7A7Rwfqm/D/KfHQIDBHU3DvDIzFvwqYhmBHaN2qSe8cDi8rsj8Q4aJkioD9W/ +RJwG2ca+WWpOeBt1LBrZRE2+e1303lPxbWioK/osn+0AAARAZv6eblIHDvd1SgKP +Ta+5aYdnfSSkUVwes2RrE3g9sa1LbkHACvpyYaA1ZsKjnueqw9q0tSr3IiSQ1Pnb +yHbokFafgSViMJPbWtWv0d+Nr8piFa8bdNVCtTfwntnHqqHfsMhdBwoH8feq028K +yqC2kDJq/f22puO3ANOp8cwzuVGRaalDToY7U1umNV1xrv70iyOL/ZRxYIgltV0a +ZlxZ1xLC4vRcfcczRI/N3+A9w/rBinvxw2d5GYYjRlpjw9xFU6YskbuG3XExus+C +F0WcJ0DMduSpFpCELuqnD2RuDQKd01IHajzkLuRcBrglUC49+IBNJVNtXkHu3TVH +OllgHFMp2GaD28uil50oQYYj89Ok7JNir17aUChGNmKVaEkheOiDgeLQKJRpRwu+ +fL4LJYdI2sXnxSUv2uO7qUAvdk5xX5L1QCe4keAF//T14iSt9q00MX+wRfsmP6WC +nhK7DJJwjN40RcyBrSU0IUe4oR+AIzWNVJRbb1QMA6mhKDICMGknOnKAgfjPYbhp +kw579C6MS4XxwvwyaZaUQy3loKfOwN7ahp+Hdzn9ojpzDH8WHdgl/UZjj8A6+xC0 +626exdhGm0HSbsIW5ZUa5CZfbwlgsmRDlzovymtJl6Qvqp0845m4H969wwOxM3yC +8NTUn2yNiZA1WBlM9hHvypU6l8M+cmvLd8aP2v54e47039f+dmeZfnF4N+7C59kA +YrUyiFmYQEZfNLQAJ4cnggLqQIkS39aYjNVDitw3xhQw9J5uaoMN819pSI4w9rp+ +20JqzRszH7dUVsIFsAwB8kc6eIuJun7sd9Z9a16vRzSP39mM4xMnjyTF0EUSG849 +7n7Kwwgi92aA+VgYCIYc4aKyBeElMTLMdCS2Zlwz3/mMEAiDn9xLE3i0anZ4AoYj +my1A9lmOB0Ms5F6NKc08riz8/ONerbqO6ArSDo1icrARWtzRsozarPdHTE29Aq9C +kHYQLJ1aCnUs/f3KLbJEjReXNFimR1L9jsxuof82PeJO9LUAsdUTmkfdh0Ya0gjl +jaYMdWm7bhoOjoFWPrhBfSlF5D7CCpiFyblio0SrE4FkCawDelhQG12HfcXi/UhF +Y10g/xnvK86ntus1Gmk/M8BLgKQq3EzWgYvlS/+fI8oNPIxZWyZjW71TAkvjxR8L +ctnh9rvCQtq38d0lQslZUvmgFZOdPK3y3H3KOZP7vldCW4giI8Dv9fgqqLK/crlT +WQWgtI0KIFtFeKreLupeMOFRW/cxdZ8Hr5cO8nJkQXGkc7H0Fm2b4IJl3g6h+iLe +qiw+aEFbNE0j80WV2skRoS+YhhaESoP+Q9S6O+o0g6M9hZpGszLGN7RgUScTq9oV +N0zMUJ1phrVR9q0DwIaxm9bnuswO/qpmkIOcO/w/eIx49HAwlzSf8em34laGJNBC +hofsGUNKy/8tNikiJClvZU9s4kwJKOHU/UlwTZyyqHewonFrqnuNxTWPcKe+3gP1 +sxTzLo9nqC1SsWIwn53UeAEJAhDorI7/L0xtmEijEyFL73ztomomHYQkCfh1i9+z +9z0pzPXAFv8mWqKhj/jWOlQoGSO+o3yOcsfCcHG0fRwrCi88Gqe1r5AcWEjlm3Ru +BfsI2CQsGk6vslxsAAUx5HMajVohSl+fb4ptpPoQi4UlafSJPCxJig== +=Cv77 +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-sample-4.enc.asc b/tests/openpgp/samplemsgs/pqc-sample-4.enc.asc new file mode 100644 index 000000000..f083a1248 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-sample-4.enc.asc @@ -0,0 +1,43 @@ +-----BEGIN PGP MESSAGE----- + +hQa7A/vNdqyZCOCUHQMDBGElA03HzQ06dUNguRZyQ2q17tO8kcuN7b0gxloxKTXC +oQSHg7r4wi0bvTG5j7wtgzIHxDZ5TCUelLXroMdseDgwCvvwf4kQRbxNeaFbb4hd +uPTz5HqOaOal5mi6jPRknAAABiCFleLITElzivvWup6puUk5Pt+FO2riLA6ckeIq +iW3yYOLxK5jFBT+yFceBAFP7PCZgQJxQ3n907ky4uHqJhgw+PQfSY9pcsyOu/1Co +t2kHUS5R9RcYFYnoEKnZFCvDiToJ5ZBvj5pfDl5fhm/tUPkjxC+rFXVYu4LqNH72 +OFQjOPlpwgSIszemGpzXjakfwX6lT8Y0dyFqH7I12SDOKo8/5XzBRMHDrkItqAC1 +s1YSyXsxrsFFvVZ85H00xwLMUfPfSt0berVcu8TFq2+mQfAnr7VsE4FsCRo3PMWw +tp98p/js5FSsE9w5LsfTDHc9A/T5B8br5+tbJKgkrLo+VK+aZkx7Crs/ibTbhbEA +8nAO8oJ0eA2Xgtrz4KIMmuFTGmqn2VWjuWaVTAy74yi+cDdAsohi0IZIb6MSoLuY +wcuG529PFocpolnHJFb7P2aPyywdxlaS6paQpVjzv7mXumvaUal8ySO9RcKDH5TG +vw+ZpXa4OJPYuiCmQJijzyjPI5vGqDirB61ijf0QicSJjK/+k3xN8EUunr5/VenI +9AGYVaw9CwP3G6Bt9Vm9IvhgmBwsmszUKNO+RVfDLeP1ygPoZaDQ4uXtNP/heZ/5 +NyWP11aoWslUPJiYZgp83rxfJNMhQTAsW5MizpD9fDlS/ERB8B1E3R2NTx7Zm/WT +4Yp/dhzXhiLhoQRsNG5k4hOHhebHl86V1bXxN1CuPs+m04Jt4a+6FgGIdujJ2L5y +XWgRifxVUC/vjyFSyxJHgNeYFpcRKpKz4I76qfC3x0SfGOW8DTRSSMX0jm/SoBZL +a21agps3DUogs2SPJ+cyNtwsxMKoSiADwqsR0sfx4dGvoKPA6YKU9RHi1PJp4RzF +13Cv+1sTVxGQaUFU3Z668bnk9V64oOe5Pbx+brjaERf4S/616yqlvZFYsiR94BDv +wknr6Z0MZ7Ldv7MVYsq9eLi+ubrjesxUIzdyWVdDQhgsH5akvo166gHN9aNBCj9z +aJtrUvz/gYDC9uidQSLY0P2rxlYwT1tZ7/cEMDuRzBA0HnukpLLrheNne7+bvLhl +vzjtwWFpTrUObwym/AEWHwycxyFsSUlNL82TBd7jrp/dm12Q73rdzmcn3pK/oRrr +Cwq/yxtiNyPRFx3VyfnvIJp6nkSEZniTIaI0pYlSet3KbIqInM7m8PvZHS+Yk0dV +qwn09ZdNlXhvuLcoJoQH5tE4KpG1Q4LZVwWVo6iPjkceDXapElsyc9eRuDGzZn91 +duBUCsburQyl2psCWkP6CIB7qkI7cEknSj7qLIDVQ6GA2SooZt3iX3p8+M0pJxx2 +OdiBzufreUidS5hdVl1VGjEpibdQVP6xyEokTt+hQSxt33ZFJVPlIPbuN0Hf8Ze3 +kSo4IKhHIBsObxULAa0dEH0qJ85Tsx8ZSHXcnHROUET/koa8Sd12DnFeSZDhHqd+ +efv5Go6XxqDqXcvmYfO5UbentNRoov29BrItYK1KLZI2yxH5aOfmIqwY0ZdRKknI +DLsTummH+fTcsQkImaJ/1lWnQ9PhjDXDQybRJ1kvri+37eg7tsBE3lqOpcbSyDXA +guWLrt37ufGY4dgSea2sCb7MBWjU7JgrqVjrq6sokmiXEJn0Ld2UFDwitkSTnXGJ +xl3MLzPy0xYtNynk7gsgKAZm7YKCqtCPLNXF5h7CBlZM+kedms51t4a/Ng9E27gB +PfuLrgIwbchrLt2NwtKckYczQMskoz5KTRfBweN9H+Uk8Pryb2bdC8V76GKdAG7x +qBF8d+AqE5miCIMrWuhhmx2J4w55PGrSC3Qgee+BdKjlS24DEaApAFIVTWlFvqmi +hRcFQtZXwGPBQcvlj0bSSps6GGoZN+WkEv1vBjeWTLAMxcnOskWUvmnFIO4HoM3V +fD2k4OedR71UdJLBJ/Ql+3dp/LeBbFGjCnNNI3s2+whYdrPb1AkJQkvjZCUSpikA +LauqXXdNzTtWmu691Wm8MMekHIB2v8Px1xsieoTv9zTiEy4Zil0szsTgrFPtl7fn +0Sru5FE8vcjR24UDUWRcKyYANLRyqFUPpBnIfKu3h12s1o5WW5ZHyj3x//2EpWnL +OcLlzgkoxLwWQrq4DbM1mupY/MwGRAdkSVtLjgTGZAlTRWBo0oPmUi+bP/Mc8dRs +AQkCEEE0J1X8U7mB0Y4ovh04SLU/DvVDaqwjTEktsZoa4zJ6a+TpTcQAZLR2yRxM +NYLUZPuJikov/9A/nx3ujQsGipXdjIyLTpr0RhxFOBlm5vV0+O5a/zR9vM8q7QHF +se4uGUbzOJfEhVQ7 +=rlF7 +-----END PGP MESSAGE----- diff --git a/tests/openpgp/samplemsgs/pqc-sample-5.enc.asc b/tests/openpgp/samplemsgs/pqc-sample-5.enc.asc new file mode 100644 index 000000000..eb20f9791 --- /dev/null +++ b/tests/openpgp/samplemsgs/pqc-sample-5.enc.asc @@ -0,0 +1,45 @@ +-----BEGIN PGP MESSAGE----- + +hQbbA8pEte1D0zKQHQQDBD2VswlYqdsXhS8MJSsUJ6Hmg0uyG0Ovz8Tkifmqkq43 +Tkc/i1RXTb6JPmBsEipfJ7kjmHG7FrQphfMfAU7fdaU1LC2c0cHADw96f2dgQ3qS +c0YsFLzpJDu+t9KPnGyHGDSZvNmTfNM15WoGqyQQvAefuvkJB4e5x7GYmD8a/a21 +AAAGIFGuzK90gQgSBR5LxVwmtc3oM22nKb6MTooMs+IG+SFXB2vIZDUaYO7qdPrv +NhUH584QCPbrUSk7FcPddHSwdK5+ZG5UG+aYqzDfhyGImyuMwuCMzUpVNX46ENSp +LWu0ZQiixmasjZYf0JfiNbW2KLhDMjmwuLSKK9Q/J8yfgIsqKD8NSr0mESkuA/wr +jXXhyouZzIgC0l7iNsKdWzUrjvSptA+a/nyOjnVHFrvfW58SmgRMuw4Oju4DbR3o +4u0RtKC94f599BfDnpozsHVaDqbcMI92aESDiOIiXC1QLS1I4moasAaL2r43c7cK +rmlEoqtNP2vDcjYeHopwkzJPchXBT2r0k0+1ncUUCpIiKfdv4jkb02prT8pU6e7R +D6IkGlxFZ5yMkjvZdIUxdVdSzhCJHdIcQTltx5HVtpZcdFcjYHR5yW/5r5T8wBg6 +M1a5ZAVryuJivQCszOvSDEeTQ60/yLPl2FtpuN4zLRw2+Pg6VeYYf96dYO4ez8eY +fLuNlZiljmKcl94vf5eLCf4qIGcKKn7Tse3Rk2sCX9kib9AsJ/8sQCqg09KtMHF2 +YY6PyiMMyxiDXV3Gr5IXgd0gWly1b2dTVcxrlOebCWgC+oQmhU7weCME09TDOJrT +QxZVFtIY0gyZQBLIjnoEX1ta49VAHk0JFfKywHWNfzWh11/rY6rbacD5kto9H8ga +4ox9WsG1Wlkpi6eyFIzc/mcPLkzdK5Z4GKIc+rz6OXUpypUBsbcNpihYmi84vdbv +SFxwabc7GRumcAbyGsn5Tfc1ByQ+iZlQFl1moYgOq3aRbvWzgQ18QPsDprlfeOL1 ++YVSq4/EemzEntnfpk63RtoK0gJpLTy5V8wVwqJ5hzvTYYk9i9U72IuAp9uYYnNj +OlkGU4UYk3RR0qFt092zjXIkPEg+7CvnOdSEtFMhJvrSn0Du4X87c63rypF+Tr41 +mW2v1AE4Zpr3hJCf+d02X9RCSEiGMJwLUGk/uYNtwYfSyNldJI25xwHQizhcgHSV +mA0NEdvAWuMbzu9q2GOvCATofd9G1NXQ+bHYlDh+JRP0DZpyBSefRsNg2YwByUDG +XVlugx6mP5OiRUVMI4zOfCAsvaQ+qaKgccD2wJabhcbJBCW2L0WsnrW+0Tebac9S +tCOxfIpxxGCSiRz02Hw7auqoQwOq47f1uuKH0zZ2WuRO75IIF4/D+/tm5Ba+6Iz5 +L811hIUTvDesXFO7O4EXB8TAh5dLqEWTqCR8D6mmT+KWGf/9VEAJ8KzELdKgdMC4 +LOuVDce3SWXp+S692RxrPJrUMsNDbYJpeFkTR+6gpQqDDpQybdvXJgRaTsb0Sull +UtmjgkOZAFrZ5Go6wcNvK3KCP6136oStM/4kEJvdL0qpN/u2G6kgmG+VCvwjPCng +dRMqO2F5Coj5LbviRwFsHPdf2WgDuOeq+KmDIcryKFR+7s7r/98m7oOYpJDXQDpO +xIki9Y+Aou/8v389g7lWoFqKLPzYGJbVo+1i4tMFlQ4WJ8IJVomRhFJP5xEWHzY5 +3JsDTG6077ftm7wSfx4p3P3sTtjZRo+dnuvsHPeKjLvUCdjLveCls97DMu9enk01 +sZxCVO4anSBzrWWe/dNPFHvV+le+/2tF+wDIk8xJIZKqQn9O26Edm/E7M0VldtHS +nbq0IMNjID3tSDevLFN4HSvEKAZpNsTS4f1Z/DuQndGVUBEgStPF6/iBOKcfZ7Eo +dqA4/o0nhBPmw7/EhZJ9TQgS0hH//PlfXteMpVxsHh3O0B8gIgAxbgJsn8JO8Owd +6OCzq3TQfwE3bS7dgyAzLZ5E03ZmVwnQ7GBSLvpYCi3S4S33Ki4qsqSVm23yYo9n +FzE8iVcIvWzkHhvvWf32cv27cbbtdrVXKXtCuYw1gxe47ilGkP2GWUhShJKv5rrS +MMkIctimmDaAApNFnIjZNIzGTmBkMxpkaVWIKL8lofOFlcuB9pl6l3hEUto8MB+7 +SVmzBnGFJwkvjnbIKDxB7myo882x+BBDgp4niECLyWfUuGk0q1x0lYNL7Njya8x/ +Sbtolk1l0BP4z/seUAwxKQyZAtOh55X0V1jPsdnwLzWMLMvbCSgAuu11jblp33oF +eWKn7tHsKmS2EMoas1AnEVW63Xt8meBXrzKeZV6X1K0BCQIQzhAKL6eK3Er01f1M +SWatf3nuMir/gpVxHelc5KE8CiLso+4+LxoTjsfMU+usFCJCP296eEOeE0e+tJ5g +mQp5WQNloshZPeyKknYYOnb4k8LZL5rwuB4kXCrJaARp0Wvatt3jtTccYk2bjo91 +Q1vjARFTcW45x5vD3tH55CBaWrveU08iZA4Dn95rWpoRi5q5JxV/DSHugSG1uYBP +MQHiPA1Y6KsYPhsQYg== +=aAoH +-----END PGP MESSAGE----- diff --git a/tests/tpm2dtests/defs.scm b/tests/tpm2dtests/defs.scm index 0fef71806..a913818f5 100644 --- a/tests/tpm2dtests/defs.scm +++ b/tests/tpm2dtests/defs.scm @@ -217,13 +217,14 @@ (define (gpg-pipe args0 args1 errfd) (lambda (source sink) (let* ((p (pipe)) - (task0 (spawn-process-fd `(,@GPG ,@args0) + (task0 (process-spawn-fd `(,@GPG ,@args0) source (:write-end p) errfd)) (_ (close (:write-end p))) - (task1 (spawn-process-fd `(,@GPG ,@args1) + (task1 (process-spawn-fd `(,@GPG ,@args1) (:read-end p) sink errfd))) (close (:read-end p)) - (wait-processes (list GPG GPG) (list task0 task1) #t)))) + (process-wait task0 #t) + (process-wait task1 #t)))) ;; ;; Do we have a software tpm diff --git a/tools/Makefile.am b/tools/Makefile.am index 769a81a00..9321da9e3 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -18,7 +18,7 @@ EXTRA_DIST = \ Manifest watchgnupg.c no-libgcrypt.c \ - addgnupghome applygnupgdefaults \ + addgnupghome applygnupgdefaults gpg-authcode-sign.sh \ lspgpot mail-signed-keys convert-from-106 sockprox.c \ ccidmon.c ChangeLog-2011 \ gpg-connect-agent-w32info.rc gpg-connect-agent.w32-manifest.in \ @@ -54,6 +54,7 @@ endif AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS) sbin_SCRIPTS = addgnupghome applygnupgdefaults +bin_SCRIPTS = gpg-authcode-sign.sh if BUILD_WKS_TOOLS gpg_wks_server = gpg-wks-server diff --git a/tools/call-dirmngr.c b/tools/call-dirmngr.c index c0ddcf568..d85801530 100644 --- a/tools/call-dirmngr.c +++ b/tools/call-dirmngr.c @@ -65,7 +65,8 @@ connect_dirmngr (assuan_context_t *r_ctx) err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, NULL, - opt.autostart, opt.verbose, opt.debug_ipc, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, opt.debug_ipc, NULL, NULL); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c index f6ce565c3..f8557e32b 100644 --- a/tools/card-call-scd.c +++ b/tools/card-call-scd.c @@ -306,7 +306,8 @@ start_agent (unsigned int flags) opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, NULL); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_AGENT) { diff --git a/tools/gpg-auth.c b/tools/gpg-auth.c index f433ba220..a818bee5d 100644 --- a/tools/gpg-auth.c +++ b/tools/gpg-auth.c @@ -396,7 +396,7 @@ start_agent (assuan_context_t *ctx_p) opt.agent_program, NULL, NULL, session_env, - opt.autostart, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, !opt.quiet, 0, NULL, NULL); diff --git a/tools/gpg-authcode-sign.sh b/tools/gpg-authcode-sign.sh new file mode 100644 index 000000000..0f6a34824 --- /dev/null +++ b/tools/gpg-authcode-sign.sh @@ -0,0 +1,257 @@ +#!/bin/sh +# gpg-authcode-sign.sh - Wrapper for osslsigncode +# Copyright (C) 2024 g10 Code GmbH +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +VERSION=2024-03-25 +PGM=gpg-authcode-sign.sh + +set -e + +usage() +{ + cat <<EOF +Usage: $PGM [OPTIONS] FILE_TO_SIGN SIGNED_FILE +Options: + [--desc=STRING] Include STRING as description (default=$url) + [--url=STRING] Include STRING as URL (default=$desc) + [--stamp] Use a stamp file to avoid double signing + [--dry-run] Do not actually run osslsigncode + [--template] Print a template for ~/.gnupg-autogenrc + [--version] Print version and exit +EOF + exit $1 +} + + +# The information required to sign the tarballs and binaries +# are expected in the developer specific file ~/.gnupg-autogen.rc". +# Here is an example: +print_autogenrc_template() +{ +cat <<EOF +# Location of the released tarball archives. Note that this is an +# internal archive and before uploading this to the public server, +# manual tests should be run and the git release tagged and pushed. +# This is greped by the Makefile. +RELEASE_ARCHIVE=foo@somehost:tarball-archive + +# The key used to sign the GnuPG sources. +# This is greped by the Makefile. +RELEASE_SIGNKEY=6DAA6E64A76D2840571B4902528897B826403ADA + +# The key used to sign the VERSION files of some MSI installers. +VERSION_SIGNKEY=02F38DFF731FF97CB039A1DA549E695E905BA208 + +# For signing Windows binaries we need to employ a Windows machine. +# We connect to this machine via ssh and take the connection +# parameters via .ssh/config. For example a VM could be specified +# like this: +# +# Host authenticode-signhost +# HostName localhost +# Port 27042 +# User gpgsign +# +# Depending on the used token it might be necessary to allow single +# signon and unlock the token before running the make. The following +# variable references this entry. This is greped by the Makefile. +# To enable this use authenticode-signhost as value. +AUTHENTICODE_SIGNHOST= + +# The name of the signtool as used on Windows. +# This is greped by the Makefile. +AUTHENTICODE_TOOL="C:\Program Files (x86)\Windows Kits\10\bin\signtool.exe" + +# The URL for the timestamping service +AUTHENTICODE_TSURL=http://rfc3161timestamp.globalsign.com/advanced + +# To use osslsigncode the follwing entries are required and +# an empty string must be given for AUTHENTICODE_SIGNHOST. +# They are greped by the Makefile. For example: +#AUTHENTICODE_KEY=/home/foo/.gnupg/my-authenticode-key.p12 +#AUTHENTICODE_CERTS=/home/foo/.gnupg/my-authenticode-certs.pem + +# If a smartcard is used for the Authenticode signature these +# entries are required instead (remove comment). +#AUTHENTICODE_KEY=card +AUTHENTICODE_CERTS=/home/foo/.gnupg/my_authenticode_cert.pem +OSSLSIGNCODE=/usr/bin/osslsigncode +OSSLPKCS11ENGINE=/usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so +SCUTEMODULE=/usr/local/lib/scute.so + +# Signing can also be disabled: +AUTHENTICODE_KEY=none + +# +EOF +} + + +autogenrc="$HOME/.gnupg-autogen.rc" +dryrun= +stamp= +buildtype= +# Set defaults accrding to our build system. +if [ -n "$abs_top_srcdir" -a -f "$abs_top_srcdir/packages/BUILDTYPE" ]; then + buildtype=$(cat "$abs_top_srcdir/packages/BUILDTYPE") +elif [ -f "../packages/BUILDTYPE" ]; then + buildtype=$(cat "../packages/BUILDTYPE") +elif [ -f "packages/BUILDTYPE" ]; then + buildtype=$(cat "packages/BUILDTYPE") +fi +case "$buildtype" in + vsd) + desc="GnuPG VS-Desktop" + url="https://gnupg.com" + ;; + gpd) + desc="GnuPG Desktop" + url="https://gnupg.com" + ;; + default|gpg4win) + desc="Gpg4win" + url="https://gpg4win.org" + ;; + *) + desc="GnuPG" + url="https://gnupg.org" + ;; +esac + +while [ $# -gt 0 ]; do + case "$1" in + --*=*) + optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` + ;; + *) + optarg="" + ;; + esac + + case $1 in + --desc=*) + desc="$optarg" + ;; + --url=*) + url="$optarg" + ;; + --dry-run|-n) + dryrun=yes + ;; + --stamp) + stamp=yes + ;; + --help|-h) + usage 0 + ;; + --version) + echo $VERSION + exit 0 + ;; + --template) + print_autogenrc_template + exit 0 + ;; + --*) + usage 1 1>&2 + ;; + *) + break + ;; + esac + shift +done + +if [ $# -ne 2 ]; then + usage 1 1>&2 +fi +inname="$1" +outname="$2" +shift + +if [ ! -f $autogenrc ]; then + echo >&2 "$PGM: error: '$autogenrc' missing" + echo >&2 "$PGM: hint: use option --template" + exit 1 +fi + + +for v in AUTHENTICODE_SIGNHOST AUTHENTICODE_TOOL AUTHENTICODE_TSURL \ + AUTHENTICODE_KEY AUTHENTICODE_CERTS VERSION_SIGNKEY \ + OSSLSIGNCODE OSSLPKCS11ENGINE SCUTEMODULE ; do + eval $v=$(grep '^[[:blank:]]*'$v'[[:blank:]]*=' "$autogenrc"|cut -d= -f2\ + |sed -e 's,\\,\\\\,g'| sed -e 's,^",'\', -e 's,"$,'\',) +done + + +if [ "$stamp" = yes ]; then + if [ "$outname.asig-done" -nt "$outname" ]; then + echo >&2 "$PGM: file is '$outname' is already signed" + exit 0 + fi +fi + +if [ -n "$dryrun" ]; then + + echo >&2 "$PGM: would sign: '$inname' to '$outname'" + +elif [ -n "$AUTHENTICODE_SIGNHOST" ]; then + + echo >&2 "$PGM: Signing via host $AUTHENTICODE_SIGNHOST" + + scp "$inname" "$AUTHENTICODE_SIGNHOST:a.exe" + # Invoke command on Windows via ssh + ssh "$AUTHENTICODE_SIGNHOST" \""$AUTHENTICODE_TOOL"\" sign \ + /v /sm \ + /a /n '"g10 Code GmbH"' \ + /tr \""$AUTHENTICODE_TSURL"\" /td sha256 \ + /d \""$desc"\" \ + /fd sha256 /du https://gnupg.com a.exe + scp "$AUTHENTICODE_SIGNHOST:a.exe" "$outname" + +elif [ "$AUTHENTICODE_KEY" = card ]; then + + echo >&2 "$PGM: Signing using a card: '$inname'" + + "$OSSLSIGNCODE" sign \ + -pkcs11engine "$OSSLPKCS11ENGINE" \ + -pkcs11module "$SCUTEMODULE" \ + -certs "$AUTHENTICODE_CERTS" \ + -h sha256 -n "$desc" -i "$url" \ + -ts "$AUTHENTICODE_TSURL" \ + -in "$inname" -out "$outname.tmp" + cp "$outname.tmp" "$outname" + rm "$outname.tmp" + +elif [ "$AUTHENTICODE_KEY" = none ]; then + + echo >&2 "$PGM: Signing disabled; would sign: '$inname'" + [ "$inname" != "$outname" ] && cp "$inname" "$outname" + +else + + echo >&2 "$PGM: Signing using key $AUTHENTICODE_KEY" + osslsigncode sign -certs "$AUTHENTICODE_CERTS" \ + -pkcs12 "$AUTHENTICODE_KEY" -askpass \ + -ts "$AUTHENTICODE_TSURL" \ + -h sha256 -n "$desc" -i "$url" \ + -in "$inname" -out "$outname.tmp" + cp "$outname.tmp" "$outname" + rm "$outname.tmp" + +fi + +if [ -z "$dryrun" ]; then + [ "$stamp" = yes ] && touch "$outname.asig-done" + echo >&2 "$PGM: signed file is '$outname'" +fi + +# eof diff --git a/tools/gpg-card.c b/tools/gpg-card.c index 22b95d0d7..8b3a3082b 100644 --- a/tools/gpg-card.c +++ b/tools/gpg-card.c @@ -315,9 +315,9 @@ main (int argc, char **argv) /* Set defaults for non given options. */ if (!opt.gpg_program) - opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); + opt.gpg_program = xstrdup (gnupg_module_name (GNUPG_MODULE_NAME_GPG)); if (!opt.gpgsm_program) - opt.gpgsm_program = gnupg_module_name (GNUPG_MODULE_NAME_GPGSM); + opt.gpgsm_program = xstrdup (gnupg_module_name (GNUPG_MODULE_NAME_GPGSM)); /* Now build the list of commands. We guess the size of the array * by assuming each item is a complete command. Obviously this will @@ -836,6 +836,21 @@ list_one_kinfo (card_info_t info, key_info_t kinfo, } +/* Return the retired key number if KEYREF is for a retired key; 0 if + * not. */ +static int +piv_keyref_is_retired (const char *keyref) +{ + if (!strncmp (keyref, "PIV.8", 5) + && keyref[5] >= '2' && hexdigitp (keyref + 5)) + return xtoi_1 (keyref+5) - 1; + else if (!strncmp (keyref, "PIV.9", 5) + && keyref[5] >= '0' && keyref[5] <= '5') + return atoi_1 (keyref+5) + 15; + else + return 0; +} + /* List all keyinfo in INFO using the list of LABELS. */ static void list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp, @@ -843,6 +858,7 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp, { key_info_t kinfo; int idx, i, j; + int rn; /* Print the keyinfo. We first print those we known and then all * remaining item. */ @@ -864,9 +880,15 @@ list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp, { if (kinfo->xflag) continue; - tty_fprintf (fp, "Key %s", kinfo->keyref); - for (i=4+strlen (kinfo->keyref), j=0; i < 18; i++, j=1) - tty_fprintf (fp, j? ".":" "); + if (info->apptype == APP_TYPE_PIV + && (rn = piv_keyref_is_retired (kinfo->keyref))) + tty_fprintf (fp, "Key retired %2d ...", rn); + else + { + tty_fprintf (fp, "Key %s", kinfo->keyref); + for (i=4+strlen (kinfo->keyref), j=0; i < 18; i++, j=1) + tty_fprintf (fp, j? ".":" "); + } tty_fprintf (fp, ":"); list_one_kinfo (info, kinfo, NULL, fp, no_key_lookup, create_shadow); } @@ -2218,13 +2240,15 @@ cmd_writecert (card_info_t info, char *argstr) && ascii_memistr (data, datalen, "-----END CERTIFICATE-----") && !memchr (data, 0, datalen) && !memchr (data, 1, datalen)) { - struct b64state b64; + gpgrt_b64state_t b64; - err = b64dec_start (&b64, ""); - if (!err) - err = b64dec_proc (&b64, data, datalen, &datalen); + b64 = gpgrt_b64dec_start (""); + if (!b64) + err = gpg_error_from_syserror (); + else + err = gpgrt_b64dec_proc (b64, data, datalen, &datalen); if (!err) - err = b64dec_finish (&b64); + err = gpgrt_b64dec_finish (b64); if (err) goto leave; } @@ -3779,7 +3803,7 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm) char **argarray; ccparray_t ccp; const char **argv = NULL; - pid_t pid; + gnupg_process_t proc; int i; if (!info) @@ -3807,15 +3831,15 @@ cmd_gpg (card_info_t info, char *argstr, int use_gpgsm) goto leave; } - err = gnupg_spawn_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program, - argv, NULL, (GNUPG_SPAWN_KEEP_STDOUT - |GNUPG_SPAWN_KEEP_STDERR), - NULL, NULL, NULL, &pid); + err = gnupg_process_spawn (use_gpgsm? opt.gpgsm_program:opt.gpg_program, + argv, + (GNUPG_PROCESS_STDOUT_KEEP + | GNUPG_PROCESS_STDERR_KEEP), + NULL, NULL, &proc); if (!err) { - err = gnupg_wait_process (use_gpgsm? opt.gpgsm_program:opt.gpg_program, - pid, 1, NULL); - gnupg_release_process (pid); + err = gnupg_process_wait (proc, 1); + gnupg_process_release (proc); } diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index cf4e64e2b..577b12575 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -898,8 +898,10 @@ static void do_sendfd (assuan_context_t ctx, char *line) { estream_t fp; - char *name, *mode, *p; - int rc, fd; + char *name, *p; + int rc; + char mode[32]; + es_syshd_t hd; /* Get file name. */ name = line; @@ -911,17 +913,25 @@ do_sendfd (assuan_context_t ctx, char *line) p++; /* Get mode. */ - mode = p; - if (!*mode) - mode = "r"; + if (!*p) + { + mode[0] = 'r'; + mode[1] = 0; + p = &mode[1]; + } else { - for (p=mode; *p && !spacep (p); p++) - ; - if (*p) - *p++ = 0; + int i; + for (i = 0; *p && !spacep (p); p++) + mode[i++] = *p; + mode[i] = 0; + p = &mode[i]; } +#ifdef HAVE_W32_SYSTEM + strcpy (p, ",sysopen"); +#endif + /* Open and send. */ fp = es_fopen (name, mode); if (!fp) @@ -930,15 +940,30 @@ do_sendfd (assuan_context_t ctx, char *line) name, mode, strerror (errno)); return; } - fd = es_fileno (fp); + es_syshd (fp, &hd); + +#ifdef HAVE_W32_SYSTEM + if (opt.verbose) + log_error ("file '%s' opened in \"%s\" mode, fd=%p\n", + name, mode, hd.u.handle); +#else if (opt.verbose) log_error ("file '%s' opened in \"%s\" mode, fd=%d\n", - name, mode, fd); + name, mode, hd.u.fd); +#endif - rc = assuan_sendfd (ctx, INT2FD (fd) ); +#ifdef HAVE_W32_SYSTEM + rc = assuan_sendfd (ctx, hd.u.handle); + if (rc) + log_error ("sending descriptor %p failed: %s\n", hd.u.handle, + gpg_strerror (rc)); +#else + rc = assuan_sendfd (ctx, hd.u.fd); if (rc) - log_error ("sending descriptor %d failed: %s\n", fd, gpg_strerror (rc)); + log_error ("sending descriptor %d failed: %s\n", hd.u.fd, + gpg_strerror (rc)); +#endif es_fclose (fp); } @@ -1013,8 +1038,9 @@ do_open (char *line) #if defined(HAVE_W32_SYSTEM) { HANDLE prochandle, handle, newhandle; + char numbuf[35]; - handle = (void*)_get_osfhandle (fd); + handle = (HANDLE)_get_osfhandle (fd); prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, server_pid); if (!prochandle) @@ -1035,11 +1061,13 @@ do_open (char *line) } CloseHandle (prochandle); open_fd_table[fd].handle = newhandle; + + snprintf (numbuf, sizeof numbuf, "%p", open_fd_table[fd].handle); + set_var (varname, numbuf); } if (opt.verbose) - log_info ("file '%s' opened in \"%s\" mode, fd=%d (libc=%d)\n", - name, mode, (int)open_fd_table[fd].handle, fd); - set_int_var (varname, (int)open_fd_table[fd].handle); + log_info ("file '%s' opened in \"%s\" mode, fd=%p (libc=%d)\n", + name, mode, open_fd_table[fd].handle, fd); #else /* Unix */ if (opt.verbose) log_info ("file '%s' opened in \"%s\" mode, fd=%d\n", @@ -1060,13 +1088,28 @@ do_open (char *line) static void do_close (char *line) { - int fd = atoi (line); + int fd; #ifdef HAVE_W32_SYSTEM int i; + gpg_error_t err; + es_syshd_t syshd; + + err = gnupg_parse_fdstr (line, &syshd); + if (err) + { + log_error ("given fd (system handle) is not valid\n"); + return; + } + + if (syshd.type == ES_SYSHD_FD) + { + log_error ("given fd is stdin/out/err\n"); + return; + } for (i=0; i < DIM (open_fd_table); i++) - if ( open_fd_table[i].inuse && open_fd_table[i].handle == (void*)fd) + if (open_fd_table[i].inuse && open_fd_table[i].handle == syshd.u.handle) break; if (i < DIM (open_fd_table)) fd = i; @@ -1075,6 +1118,8 @@ do_close (char *line) log_error ("given fd (system handle) has not been opened\n"); return; } +#else + fd = atoi (line); #endif if (fd < 0 || fd >= DIM (open_fd_table)) @@ -1105,7 +1150,7 @@ do_showopen (void) if (open_fd_table[i].inuse) { #ifdef HAVE_W32_SYSTEM - printf ("%-15d (libc=%d)\n", (int)open_fd_table[i].handle, i); + printf ("%p (libc=%d)\n", open_fd_table[i].handle, i); #else printf ("%-15d\n", i); #endif @@ -2302,14 +2347,14 @@ start_agent (void) err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, - opt.autostart, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, !opt.quiet, 0, NULL, NULL); else if (opt.use_keyboxd) err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, - opt.autostart, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, !opt.quiet, 0, NULL, NULL); else @@ -2318,7 +2363,7 @@ start_agent (void) opt.agent_program, NULL, NULL, session_env, - opt.autostart, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, !opt.quiet, 0, NULL, NULL); diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index 90f2f53d3..d6aa9d61b 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -744,7 +744,7 @@ gpg_agent_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[5]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -761,13 +761,13 @@ gpg_agent_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -777,7 +777,7 @@ scdaemon_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[9]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -805,13 +805,13 @@ scdaemon_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -822,7 +822,7 @@ tpm2daemon_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[9]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -850,13 +850,13 @@ tpm2daemon_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } #endif @@ -867,7 +867,7 @@ dirmngr_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[6]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -885,13 +885,13 @@ dirmngr_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -901,7 +901,7 @@ keyboxd_runtime_change (int killflag) gpg_error_t err = 0; const char *pgmname; const char *argv[6]; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; int i = 0; int cmdidx; @@ -919,13 +919,13 @@ keyboxd_runtime_change (int killflag) log_assert (i < DIM(argv)); if (!err) - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[cmdidx], gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); } @@ -937,7 +937,7 @@ gc_component_launch (int component) const char *pgmname; const char *argv[6]; int i; - pid_t pid; + gnupg_process_t proc = NULL; if (component < 0) { @@ -985,9 +985,9 @@ gc_component_launch (int component) argv[i] = NULL; log_assert (i < DIM(argv)); - err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid); + err = gnupg_process_spawn (pgmname, argv, 0, NULL, NULL, &proc); if (!err) - err = gnupg_wait_process (pgmname, pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) gc_error (0, 0, "error running '%s%s%s': %s", pgmname, @@ -995,7 +995,7 @@ gc_component_launch (int component) : component == GC_COMPONENT_KEYBOXD? " --keyboxd":"", " NOP", gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_release (proc); return err; } @@ -1336,8 +1336,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) const char *pgmname; const char *argv[6]; int i; - pid_t pid; - int exitcode; + gnupg_process_t proc; estream_t errfp; error_line_t errlines; @@ -1370,22 +1369,28 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) result = 0; errlines = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, NULL, &errfp, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDERR_PIPE, + NULL, NULL, &proc); if (err) result |= 1; /* Program could not be run. */ else { + gnupg_process_get_streams (proc, 0, NULL, NULL, &errfp); errlines = collect_error_output (errfp, gc_component[component].name); - if (gnupg_wait_process (pgmname, pid, 1, &exitcode)) + if (!gnupg_process_wait (proc, 1)) { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); if (exitcode == -1) result |= 1; /* Program could not be run or it terminated abnormally. */ - result |= 2; /* Program returned an error. */ + else if (exitcode) + result |= 2; /* Program returned an error. */ } - gnupg_release_process (pid); + gnupg_process_release (proc); es_fclose (errfp); } @@ -1725,8 +1730,7 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) const char *pgmname; const char *argv[2]; estream_t outfp; - int exitcode; - pid_t pid; + gnupg_process_t proc; known_option_t *known_option; gc_option_t *option; char *line = NULL; @@ -1759,14 +1763,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) /* First we need to read the option table from the program. */ argv[0] = "--dump-option-table"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { gc_error (1, 0, "could not gather option table from '%s': %s", pgmname, gpg_strerror (err)); } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); + read_line_parm.pgmname = pgmname; read_line_parm.fp = outfp; read_line_parm.line = line; @@ -1925,12 +1932,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) line_len = read_line_parm.line_len; log_assert (opt_table_used + pseudo_count == opt_info_used); + err = gnupg_process_wait (proc, 1); + if (!err) + { + int exitcode; - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) - gc_error (1, 0, "running %s failed (exitcode=%d): %s", - pgmname, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) + gc_error (1, 0, "running %s failed (exitcode=%d): %s", + pgmname, exitcode, gpg_strerror (err)); + } + gnupg_process_release (proc); /* Make the gpgrt option table and the internal option table available. */ gc_component[component].opt_table = opt_table; @@ -1940,14 +1952,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) /* Now read the default options. */ argv[0] = "--gpgconf-list"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { gc_error (1, 0, "could not gather active options from '%s': %s", pgmname, gpg_strerror (err)); } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); + while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { char *linep; @@ -2030,11 +2045,17 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed) if (es_fclose (outfp)) gc_error (1, errno, "error closing %s", pgmname); - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) - gc_error (1, 0, "running %s failed (exitcode=%d): %s", - pgmname, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); + err = gnupg_process_wait (proc, 1); + if (!err) + { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) + gc_error (1, 0, "running %s failed (exitcode=%d): %s", + pgmname, exitcode, gpg_strerror (err)); + } + gnupg_process_release (proc); /* At this point, we can parse the configuration file. */ diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 061a4f727..ac709ae21 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -343,7 +343,7 @@ list_dirs (estream_t fp, char **names, int show_config_mode) #ifdef HAVE_W32_SYSTEM tmp = read_w32_registry_string (NULL, - GNUPG_REGISTRY_DIR, + gnupg_registry_dir (), "HomeDir"); if (tmp) { @@ -352,14 +352,14 @@ list_dirs (estream_t fp, char **names, int show_config_mode) xfree (tmp); if ((tmp = read_w32_registry_string ("HKEY_CURRENT_USER", - GNUPG_REGISTRY_DIR, + gnupg_registry_dir (), "HomeDir"))) { xfree (tmp); hkcu = 1; } if ((tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE", - GNUPG_REGISTRY_DIR, + gnupg_registry_dir (), "HomeDir"))) { xfree (tmp); @@ -372,15 +372,15 @@ list_dirs (estream_t fp, char **names, int show_config_mode) "Note: homedir taken from registry key %s%s\\%s:%s\n" "\n", hkcu?"HKCU":"", hklm?"HKLM":"", - GNUPG_REGISTRY_DIR, "HomeDir"); + gnupg_registry_dir (), "HomeDir"); else log_info ("Warning: homedir taken from registry key (%s:%s) in%s%s\n", - GNUPG_REGISTRY_DIR, "HomeDir", + gnupg_registry_dir (), "HomeDir", hkcu?" HKCU":"", hklm?" HKLM":""); } else if ((tmp = read_w32_registry_string (NULL, - GNUPG_REGISTRY_DIR, + gnupg_registry_dir (), NULL))) { xfree (tmp); @@ -391,7 +391,7 @@ list_dirs (estream_t fp, char **names, int show_config_mode) "\n", GNUPG_REGISTRY_DIR); else log_info ("Warning: registry key (%s) without value in HKCU or HKLM\n", - GNUPG_REGISTRY_DIR); + gnupg_registry_dir ()); } #else /*!HAVE_W32_SYSTEM*/ @@ -1303,17 +1303,17 @@ show_versions_via_dirmngr (estream_t fp) const char *pgmname; const char *argv[2]; estream_t outfp; - pid_t pid; + gnupg_process_t proc; char *line = NULL; size_t line_len = 0; ssize_t length; - int exitcode; pgmname = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); argv[0] = "--gpgconf-versions"; argv[1] = NULL; - err = gnupg_spawn_process (pgmname, argv, NULL, 0, - NULL, &outfp, NULL, &pid); + err = gnupg_process_spawn (pgmname, argv, + GNUPG_PROCESS_STDOUT_PIPE, + NULL, NULL, &proc); if (err) { log_error ("error spawning %s: %s", pgmname, gpg_strerror (err)); @@ -1321,6 +1321,7 @@ show_versions_via_dirmngr (estream_t fp) return; } + gnupg_process_get_streams (proc, 0, NULL, &outfp, NULL); while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { /* Strip newline and carriage return, if present. */ @@ -1341,14 +1342,17 @@ show_versions_via_dirmngr (estream_t fp) pgmname, gpg_strerror (err)); } - err = gnupg_wait_process (pgmname, pid, 1, &exitcode); - if (err) + err = gnupg_process_wait (proc, 1); + if (!err) { + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); log_error ("running %s failed (exitcode=%d): %s\n", pgmname, exitcode, gpg_strerror (err)); es_fprintf (fp, "[error: can't get further info]\n"); } - gnupg_release_process (pid); + gnupg_process_release (proc); xfree (line); } @@ -1485,13 +1489,14 @@ show_other_registry_entries (estream_t outfp) static struct { int group; const char *name; + unsigned int prependregkey:1; } names[] = { { 1, "HKLM\\Software\\Gpg4win:Install Directory" }, { 1, "HKLM\\Software\\Gpg4win:Desktop-Version" }, { 1, "HKLM\\Software\\Gpg4win:VS-Desktop-Version" }, - { 1, "\\" GNUPG_REGISTRY_DIR ":HomeDir" }, - { 1, "\\" GNUPG_REGISTRY_DIR ":DefaultLogFile" }, + { 1, ":HomeDir", 1 }, + { 1, ":DefaultLogFile", 1 }, { 2, "\\Software\\Microsoft\\Office\\Outlook\\Addins\\GNU.GpgOL" ":LoadBehavior" }, { 2, "HKCU\\Software\\Microsoft\\Office\\16.0\\Outlook\\Options\\Mail:" @@ -1537,6 +1542,13 @@ show_other_registry_entries (estream_t outfp) names[idx].name, NULL); name = namebuf; } + else if (names[idx].prependregkey) + { + xfree (namebuf); + namebuf = xstrconcat ("\\", gnupg_registry_dir (), + names[idx].name, NULL); + name = namebuf; + } value = read_w32_reg_string (name, &from_hklm); if (!value) diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c index 0994322ea..7af5a2ede 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -1069,7 +1069,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, estream_t files_from_stream = NULL; estream_t outstream = NULL; int eof_seen = 0; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; unsigned int skipped_open = 0; memset (scanctrl, 0, sizeof *scanctrl); @@ -1228,7 +1228,11 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, { strlist_t arg; ccparray_t ccp; +#ifdef HAVE_W32_SYSTEM + HANDLE except[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; +#else int except[2] = { -1, -1 }; +#endif const char **argv; /* '--encrypt' may be combined with '--symmetric', but 'encrypt' @@ -1246,13 +1250,19 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, ccparray_put (&ccp, "--no"); if (opt.require_compliance) ccparray_put (&ccp, "--require-compliance"); - if (opt.status_fd != -1) + if (opt.status_fd) { static char tmpbuf[40]; + es_syshd_t hd; - snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%s", opt.status_fd); ccparray_put (&ccp, tmpbuf); - except[0] = opt.status_fd; + es_syshd (opt.status_stream, &hd); +#ifdef HAVE_W32_SYSTEM + except[0] = hd.u.handle; +#else + except[0] = hd.u.fd; +#endif } ccparray_put (&ccp, "--output"); @@ -1286,14 +1296,15 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - (GNUPG_SPAWN_KEEP_STDOUT - | GNUPG_SPAWN_KEEP_STDERR), - &outstream, NULL, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + (GNUPG_PROCESS_STDIN_PIPE + | GNUPG_PROCESS_STDOUT_KEEP + | GNUPG_PROCESS_STDERR_KEEP), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, &outstream, NULL, NULL); es_set_binary (outstream); } else if (opt.outfile) /* No crypto */ @@ -1332,23 +1343,25 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, write_progress (1, global_written_files, global_total_files); write_progress (0, global_written_data, global_total_data); - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (outstream); outstream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) log_error ("running %s failed (exitcode=%d): %s", opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); } + gnupg_process_release (proc); + proc = NULL; } if (skipped_open) @@ -1361,7 +1374,7 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, if (!err) { gpg_error_t first_err; - if (outstream != es_stdout || pid != (pid_t)(-1)) + if (outstream != es_stdout) first_err = es_fclose (outstream); else first_err = es_fflush (outstream); diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c index 936d03e3e..87113b054 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -339,7 +339,7 @@ gpgtar_extract (const char *filename, int decrypt) char *dirname = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc; char *logfilename = NULL; unsigned long long notextracted; @@ -384,7 +384,11 @@ gpgtar_extract (const char *filename, int decrypt) { strlist_t arg; ccparray_t ccp; +#ifdef HAVE_W32_SYSTEM + HANDLE except[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; +#else int except[2] = { -1, -1 }; +#endif const char **argv; ccparray_init (&ccp, 0); @@ -392,13 +396,19 @@ gpgtar_extract (const char *filename, int decrypt) ccparray_put (&ccp, "--batch"); if (opt.require_compliance) ccparray_put (&ccp, "--require-compliance"); - if (opt.status_fd != -1) + if (opt.status_fd) { static char tmpbuf[40]; + es_syshd_t hd; - snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%s", opt.status_fd); ccparray_put (&ccp, tmpbuf); - except[0] = opt.status_fd; + es_syshd (opt.status_stream, &hd); +#ifdef HAVE_W32_SYSTEM + except[0] = hd.u.handle; +#else + except[0] = hd.u.fd; +#endif } if (opt.with_log) { @@ -425,14 +435,14 @@ gpgtar_extract (const char *filename, int decrypt) goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) - | GNUPG_SPAWN_KEEP_STDERR), - NULL, &stream, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP) + | GNUPG_PROCESS_STDOUT_PIPE), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, NULL, &stream, NULL); es_set_binary (stream); } else if (filename) @@ -472,23 +482,25 @@ gpgtar_extract (const char *filename, int decrypt) header = NULL; } - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (stream); stream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + if (exitcode) log_error ("running %s failed (exitcode=%d): %s", opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); } + gnupg_process_release (proc); + proc = NULL; } leave: diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c index c5bf25825..0c5e474f3 100644 --- a/tools/gpgtar-list.c +++ b/tools/gpgtar-list.c @@ -460,7 +460,7 @@ gpgtar_list (const char *filename, int decrypt) strlist_t extheader = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; - pid_t pid = (pid_t)(-1); + gnupg_process_t proc = NULL; memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer); @@ -468,7 +468,11 @@ gpgtar_list (const char *filename, int decrypt) { strlist_t arg; ccparray_t ccp; +#ifdef HAVE_W32_SYSTEM + HANDLE except[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE }; +#else int except[2] = { -1, -1 }; +#endif const char **argv; ccparray_init (&ccp, 0); @@ -476,13 +480,19 @@ gpgtar_list (const char *filename, int decrypt) ccparray_put (&ccp, "--batch"); if (opt.require_compliance) ccparray_put (&ccp, "--require-compliance"); - if (opt.status_fd != -1) + if (opt.status_fd) { static char tmpbuf[40]; + es_syshd_t hd; - snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%s", opt.status_fd); ccparray_put (&ccp, tmpbuf); - except[0] = opt.status_fd; + es_syshd (opt.status_stream, &hd); +#ifdef HAVE_W32_SYSTEM + except[0] = hd.u.handle; +#else + except[0] = hd.u.fd; +#endif } ccparray_put (&ccp, "--output"); ccparray_put (&ccp, "-"); @@ -503,14 +513,14 @@ gpgtar_list (const char *filename, int decrypt) goto leave; } - err = gnupg_spawn_process (opt.gpg_program, argv, - except[0] == -1? NULL : except, - ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) - | GNUPG_SPAWN_KEEP_STDERR), - NULL, &stream, NULL, &pid); + err = gnupg_process_spawn (opt.gpg_program, argv, + ((filename ? 0 : GNUPG_PROCESS_STDIN_KEEP) + | GNUPG_PROCESS_STDOUT_PIPE), + gnupg_spawn_helper, except, &proc); xfree (argv); if (err) goto leave; + gnupg_process_get_streams (proc, 0, NULL, &stream, NULL); es_set_binary (stream); } else if (filename) /* No decryption requested. */ @@ -550,23 +560,24 @@ gpgtar_list (const char *filename, int decrypt) header = NULL; } - if (pid != (pid_t)(-1)) + if (proc) { - int exitcode; - err = es_fclose (stream); stream = NULL; if (err) log_error ("error closing pipe: %s\n", gpg_strerror (err)); - else + + err = gnupg_process_wait (proc, 1); + if (!err) { - err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); - if (err) - log_error ("running %s failed (exitcode=%d): %s", - opt.gpg_program, exitcode, gpg_strerror (err)); - gnupg_release_process (pid); - pid = (pid_t)(-1); + int exitcode; + + gnupg_process_ctl (proc, GNUPG_PROCESS_GET_EXIT_ID, &exitcode); + log_error ("running %s failed (exitcode=%d): %s", + opt.gpg_program, exitcode, gpg_strerror (err)); } + gnupg_process_release (proc); + proc = NULL; } leave: diff --git a/tools/gpgtar.c b/tools/gpgtar.c index ea1e1e751..f93ba2e65 100644 --- a/tools/gpgtar.c +++ b/tools/gpgtar.c @@ -127,7 +127,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oBatch, "batch", "@"), ARGPARSE_s_n (oAnswerYes, "yes", "@"), ARGPARSE_s_n (oAnswerNo, "no", "@"), - ARGPARSE_s_i (oStatusFD, "status-fd", "@"), + ARGPARSE_s_s (oStatusFD, "status-fd", "@"), ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"), ARGPARSE_s_n (oWithLog, "with-log", "@"), @@ -398,7 +398,7 @@ parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts) case oBatch: opt.batch = 1; break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; - case oStatusFD: opt.status_fd = pargs->r.ret_int; break; + case oStatusFD: opt.status_fd = pargs->r.ret_str; break; case oRequireCompliance: opt.require_compliance = 1; break; case oWithLog: opt.with_log = 1; break; @@ -480,7 +480,7 @@ main (int argc, char **argv) log_assert (sizeof (struct ustar_raw_header) == 512); /* Set default options */ - opt.status_fd = -1; + opt.status_fd = NULL; /* The configuraton directories for use by gpgrt_argparser. */ gpgrt_set_confdir (GPGRT_CONFDIR_SYS, gnupg_sysconfdir ()); @@ -512,31 +512,32 @@ main (int argc, char **argv) /* Set status stream for our own use of --status-fd. The original * status fd is passed verbatim to gpg. */ - if (opt.status_fd != -1) + if (opt.status_fd) { - int fd = translate_sys2libc_fd_int (opt.status_fd, 1); + es_syshd_t syshd; - if (!gnupg_fd_valid (fd)) - log_fatal ("status-fd is invalid: %s\n", strerror (errno)); + err = gnupg_parse_fdstr (opt.status_fd, &syshd); + if (err) + log_fatal ("status-fd is invalid: %s\n", gpg_strerror (err)); - if (fd == 1) + if (syshd.type == ES_SYSHD_FD && syshd.u.fd == 1) { opt.status_stream = es_stdout; if (!skip_crypto) log_fatal ("using stdout for the status-fd is not possible\n"); } - else if (fd == 2) + else if (syshd.type == ES_SYSHD_FD && syshd.u.fd == 2) opt.status_stream = es_stderr; else { - opt.status_stream = es_fdopen (fd, "w"); + opt.status_stream = es_sysopen (&syshd, "w"); if (opt.status_stream) es_setvbuf (opt.status_stream, NULL, _IOLBF, 0); } if (!opt.status_stream) { - log_fatal ("can't open fd %d for status output: %s\n", - fd, strerror (errno)); + log_fatal ("can't open fd %s for status output: %s\n", + opt.status_fd, strerror (errno)); } } diff --git a/tools/gpgtar.h b/tools/gpgtar.h index d86010476..4e36deaeb 100644 --- a/tools/gpgtar.h +++ b/tools/gpgtar.h @@ -45,7 +45,7 @@ struct int batch; int answer_yes; int answer_no; - int status_fd; + const char *status_fd; estream_t status_stream; int require_compliance; int with_log; diff --git a/tools/mime-parser.c b/tools/mime-parser.c index 0db1a9c23..31f766ca5 100644 --- a/tools/mime-parser.c +++ b/tools/mime-parser.c @@ -93,7 +93,7 @@ struct mime_parser_context_s unsigned int boundary : 1; } show; - struct b64state *b64state; /* NULL or malloced Base64 decoder state. */ + gpgrt_b64state_t b64state; /* NULL or malloced Base64 decoder state. */ /* A buffer for reading a mail line, */ char line[5000]; @@ -410,15 +410,11 @@ parse_message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg) { ctx->decode_part = 2; if (ctx->b64state) - b64dec_finish (ctx->b64state); /* Reuse state. */ - else - { - ctx->b64state = xtrymalloc (sizeof *ctx->b64state); - if (!ctx->b64state) - rc = gpg_error_from_syserror (); - } - if (!rc) - rc = b64dec_start (ctx->b64state, NULL); + gpgrt_b64dec_finish (ctx->b64state); /* Release. */ + + ctx->b64state = gpgrt_b64dec_start (NULL); + if (!ctx->b64state) + rc = gpg_error_from_syserror (); } free (value); /* Right, we need a plain free. */ } @@ -528,10 +524,7 @@ mime_parser_release (mime_parser_t ctx) return; if (ctx->b64state) - { - b64dec_finish (ctx->b64state); - xfree (ctx->b64state); - } + gpgrt_b64dec_finish (ctx->b64state); xfree (ctx); } @@ -661,7 +654,7 @@ process_part_data (mime_parser_t ctx, char *line, size_t *length) else if (ctx->decode_part == 2) { log_assert (ctx->b64state); - err = b64dec_proc (ctx->b64state, line, *length, &nbytes); + err = gpgrt_b64dec_proc (ctx->b64state, line, *length, &nbytes); if (err) return err; *length = nbytes; diff --git a/tools/no-libgcrypt.c b/tools/no-libgcrypt.c index 3b577567a..cbf934013 100644 --- a/tools/no-libgcrypt.c +++ b/tools/no-libgcrypt.c @@ -137,14 +137,6 @@ gcry_set_fatalerror_handler (gcry_handler_error_t fnc, void *opaque) } void -gcry_set_log_handler (gcry_handler_log_t f, void *opaque) -{ - (void)f; - (void)opaque; -} - - -void gcry_create_nonce (void *buffer, size_t length) { (void)buffer; diff --git a/tools/wks-util.c b/tools/wks-util.c index 640116ce8..4a15d672a 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -599,7 +599,7 @@ wks_armor_key (estream_t *r_newkey, estream_t key, const char *prefix) { gpg_error_t err; estream_t newkey; - struct b64state b64state; + gpgrt_b64state_t b64state; char buffer[4096]; size_t nread; @@ -615,16 +615,19 @@ wks_armor_key (estream_t *r_newkey, estream_t key, const char *prefix) if (prefix) es_fputs (prefix, newkey); - err = b64enc_start_es (&b64state, newkey, "PGP PUBLIC KEY BLOCK"); - if (err) - goto leave; + b64state = gpgrt_b64enc_start (newkey, "PGP PUBLIC KEY BLOCK"); + if (!b64state) + { + err = gpg_error_from_syserror (); + goto leave; + } do { nread = es_fread (buffer, 1, sizeof buffer, key); if (!nread) break; - err = b64enc_write (&b64state, buffer, nread); + err = gpgrt_b64enc_write (b64state, buffer, nread); if (err) goto leave; } @@ -635,7 +638,7 @@ wks_armor_key (estream_t *r_newkey, estream_t key, const char *prefix) goto leave; } - err = b64enc_finish (&b64state); + err = gpgrt_b64enc_finish (b64state); if (err) goto leave; diff --git a/tpm2d/tpm2.c b/tpm2d/tpm2.c index 3e908ddb1..d0b32ed35 100644 --- a/tpm2d/tpm2.c +++ b/tpm2d/tpm2.c @@ -695,8 +695,8 @@ TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s, { TPMT_HA hash; const int hlen = TSS_GetDigestSize (nalg); - TPM2B *digest = (TPM2B *)buf; - TPM2B *s2b; + BYTE *digest; + BYTE *s2b; int32_t size; unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES]; UINT16 bsize, written = 0; @@ -707,13 +707,12 @@ TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s, memset (null_iv, 0, sizeof (null_iv)); /* reserve space for hash before the encrypted sensitive */ - bsize = sizeof (digest->size) + hlen; - buf += bsize; + digest = buf; + bsize = sizeof (uint16_t /* TPM2B.size */) + hlen; p->size += bsize; - s2b = (TPM2B *)buf; + s2b = digest + bsize; /* marshal the digest size */ - buf = (BYTE *)&digest->size; bsize = hlen; size = 2; TSS_UINT16_Marshal (&bsize, &written, &buf, &size); @@ -721,13 +720,13 @@ TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s, /* marshal the unencrypted sensitive in place */ size = sizeof (*s); bsize = 0; - buf = s2b->buffer; + buf = s2b + offsetof (TPM2B, buffer); TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size); - buf = (BYTE *)&s2b->size; + buf = s2b; size = 2; TSS_UINT16_Marshal (&bsize, &written, &buf, &size); - bsize = bsize + sizeof (s2b->size); + bsize = bsize + sizeof (uint16_t /* TPM2B.size */); p->size += bsize; /* compute hash of unencrypted marshalled sensitive and @@ -736,7 +735,7 @@ TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s, TSS_Hash_Generate (&hash, bsize, s2b, name->size, name->name, 0, NULL); - memcpy (digest->buffer, &hash.digest, hlen); + memcpy (digest + offsetof (TPM2B, buffer), &hash.digest, hlen); gcry_cipher_open (&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE); gcry_cipher_setiv (hd, null_iv, sizeof (null_iv)); @@ -749,20 +748,20 @@ TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s, else if (symdef->algorithm == TPM_ALG_NULL) { /* Code is for debugging only, should never be used in production */ - TPM2B *s2b = (TPM2B *)buf; + BYTE *s2b = buf; int32_t size = sizeof (*s); UINT16 bsize = 0, written = 0; log_error ("Secret key sent to TPM unencrypted\n"); - buf = s2b->buffer; + buf = s2b + offsetof (TPM2B, buffer); /* marshal the unencrypted sensitive in place */ TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size); - buf = (BYTE *)&s2b->size; + buf = s2b; size = 2; TSS_UINT16_Marshal (&bsize, &written, &buf, &size); - p->size += bsize + sizeof (s2b->size); + p->size += bsize + sizeof (uint16_t /* TPM2B.size */); } else { diff --git a/tpm2d/tpm2daemon.c b/tpm2d/tpm2daemon.c index b4e6f66e7..8e16e931a 100644 --- a/tpm2d/tpm2daemon.c +++ b/tpm2d/tpm2daemon.c @@ -192,9 +192,6 @@ static gnupg_fd_t create_server_socket (const char *name, static void *start_connection_thread (void *arg); static void handle_connections (gnupg_fd_t listen_fd); -/* Pth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - static int active_connections; @@ -368,7 +365,6 @@ main (int argc, char **argv ) malloc_hooks.free = gcry_free; assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); assuan_sock_init (); setup_libassuan_logging (&opt.debug, NULL); @@ -599,6 +595,7 @@ main (int argc, char **argv ) npth_init (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* If --debug-allow-core-dump has been given we also need to switch the working directory to a place where we can actually @@ -740,6 +737,7 @@ main (int argc, char **argv ) npth_init (); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); /* Detach from tty and put process into a new session. */ if (!nodetach ) @@ -1012,7 +1010,7 @@ start_connection_thread (void *arg) && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_nonce)) { log_info (_("error reading nonce on fd %d: %s\n"), - FD2INT (ctrl->thread_startup.fd), strerror (errno)); + FD_DBG (ctrl->thread_startup.fd), strerror (errno)); assuan_sock_close (ctrl->thread_startup.fd); xfree (ctrl); return NULL; @@ -1023,7 +1021,7 @@ start_connection_thread (void *arg) tpm2d_init_default_ctrl (ctrl); if (opt.verbose) log_info (_("handler for fd %d started\n"), - FD2INT (ctrl->thread_startup.fd)); + FD_DBG (ctrl->thread_startup.fd)); /* If this is a pipe server, we request a shutdown if the command handler asked for it. With the next ticker event and given that @@ -1035,7 +1033,7 @@ start_connection_thread (void *arg) if (opt.verbose) log_info (_("handler for fd %d terminated\n"), - FD2INT (ctrl->thread_startup.fd)); + FD_DBG (ctrl->thread_startup.fd)); tpm2d_deinit_default_ctrl (ctrl); xfree (ctrl); @@ -1154,7 +1152,7 @@ handle_connections (gnupg_fd_t listen_fd) if (listen_fd != GNUPG_INVALID_FD) { FD_SET (FD2INT (listen_fd), &fdset); - nfd = FD2INT (listen_fd); + nfd = FD2NUM (listen_fd); } for (;;) @@ -1238,8 +1236,8 @@ handle_connections (gnupg_fd_t listen_fd) gnupg_fd_t fd; plen = sizeof paddr; - fd = INT2FD (npth_accept (FD2INT (listen_fd), - (struct sockaddr *)&paddr, &plen)); + fd = assuan_sock_accept (listen_fd, + (struct sockaddr *)&paddr, &plen); if (fd == GNUPG_INVALID_FD) { log_error ("accept failed: %s\n", strerror (errno)); @@ -1255,7 +1253,7 @@ handle_connections (gnupg_fd_t listen_fd) char threadname[50]; npth_t thread; - snprintf (threadname, sizeof threadname, "conn fd=%d", FD2INT (fd)); + snprintf (threadname, sizeof threadname, "conn fd=%d", FD_DBG (fd)); ctrl->thread_startup.fd = fd; ret = npth_create (&thread, &tattr, start_connection_thread, ctrl); if (ret) |