diff --git a/agent/agent.h b/agent/agent.h index 4ed8c7fe6..a420baed9 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -1,490 +1,491 @@ /* agent.h - Global definitions for the agent * Copyright (C) 2001, 2002, 2003, 2005, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef AGENT_H #define AGENT_H #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGAGENT #include #define map_assuan_err(a) \ map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) #include #include #include "../common/util.h" #include "../common/membuf.h" #include "../common/sysutils.h" /* (gnupg_fd_t) */ #include "../common/session-env.h" #include "../common/shareddefs.h" /* To convey some special hash algorithms we use algorithm numbers reserved for application use. */ #ifndef GCRY_MODULE_ID_USER #define GCRY_MODULE_ID_USER 1024 #endif #define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1) /* Maximum length of a digest. */ #define MAX_DIGEST_LEN 64 /* A large struct name "opt" to keep global flags */ struct { unsigned int debug; /* Debug flags (DBG_foo_VALUE) */ int verbose; /* Verbosity level */ int quiet; /* Be as quiet as possible */ int dry_run; /* Don't change any persistent data */ int batch; /* Batch mode */ const char *homedir; /* Configuration directory name */ /* True if we are listening on the standard socket. */ int use_standard_socket; /* True if we handle sigusr2. */ int sigusr2_enabled; /* Environment settings gathered at program start or changed using the Assuan command UPDATESTARTUPTTY. */ session_env_t startup_env; char *startup_lc_ctype; char *startup_lc_messages; /* Filename of the program to start as pinentry. */ const char *pinentry_program; /* Filename of the program to handle smartcard tasks. */ const char *scdaemon_program; int disable_scdaemon; /* Never use the SCdaemon. */ int no_grab; /* Don't let the pinentry grab the keyboard */ /* The name of the file pinentry shall tocuh before exiting. If this is not set the filoe name of the standard socket is used. */ const char *pinentry_touch_file; /* The default and maximum TTL of cache entries. */ unsigned long def_cache_ttl; /* Default. */ unsigned long def_cache_ttl_ssh; /* for SSH. */ unsigned long max_cache_ttl; /* Default. */ unsigned long max_cache_ttl_ssh; /* for SSH. */ /* Flag disallowing bypassing of the warning. */ int enforce_passphrase_constraints; /* The require minmum length of a passphrase. */ unsigned int min_passphrase_len; /* The minimum number of non-alpha characters in a passphrase. */ unsigned int min_passphrase_nonalpha; /* File name with a patternfile or NULL if not enabled. */ const char *check_passphrase_pattern; /* If not 0 the user is asked to change his passphrase after these number of days. */ unsigned int max_passphrase_days; /* If set, a passphrase history will be written and checked at each passphrase change. */ int enable_passhrase_history; int running_detached; /* We are running detached from the tty. */ /* If this global option is true, the passphrase cache is ignored for signing operations. */ int ignore_cache_for_signing; /* If this global option is true, the user is allowed to interactively mark certificate in trustlist.txt as trusted. */ int allow_mark_trusted; /* If this global option is true, the Assuan command PRESET_PASSPHRASE is allowed. */ int allow_preset_passphrase; /* If this global option is true, the Assuan option pinentry-mode=loopback is allowed. */ int allow_loopback_pinentry; int keep_tty; /* Don't switch the TTY (for pinentry) on request */ int keep_display; /* Don't switch the DISPLAY (for pinentry) on request */ /* This global option enables the ssh-agent subsystem. */ int ssh_support; } opt; /* Bit values for the --debug option. */ #define DBG_COMMAND_VALUE 1 /* debug commands i/o */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_ASSUAN_VALUE 1024 /* Enable Assuan debugging. */ /* Test macros for the debug option. */ #define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE) /* Forward reference for local definitions in command.c. */ struct server_local_s; /* Declaration of objects from command-ssh.c. */ struct ssh_control_file_s; typedef struct ssh_control_file_s *ssh_control_file_t; /* Forward reference for local definitions in call-scd.c. */ struct scd_local_s; /* Collection of data per session (aka connection). */ struct server_control_s { /* Private data used to fire up the connection thread. We use this structure do avoid an extra allocation for only a few bytes while spawning a new connection thread. */ struct { gnupg_fd_t fd; } thread_startup; /* Private data of the server (command.c). */ struct server_local_s *server_local; /* Private data of the SCdaemon (call-scd.c). */ struct scd_local_s *scd_local; /* Environment settings for the connection. */ session_env_t session_env; char *lc_ctype; char *lc_messages; /* The current pinentry mode. */ pinentry_mode_t pinentry_mode; /* The TTL used for the --preset option of certain commands. */ int cache_ttl_opt_preset; /* Information on the currently used digest (for signing commands). */ struct { int algo; unsigned char value[MAX_DIGEST_LEN]; int valuelen; int raw_value: 1; } digest; unsigned char keygrip[20]; int have_keygrip; /* A flag to enable a hack to send the PKAUTH command instead of the PKSIGN command to the scdaemon. */ int use_auth_call; /* A flag to inhibit enforced passphrase change during an explicit passwd command. */ int in_passwd; /* The current S2K which might be different from the calibrated count. */ unsigned long s2k_count; }; /* Information pertaining to pinentry requests. */ struct pin_entry_info_s { int min_digits; /* min. number of digits required or 0 for freeform entry */ int max_digits; /* max. number of allowed digits allowed*/ int max_tries; /* max. number of allowed tries. */ int failed_tries; /* Number of tries so far failed. */ int with_qualitybar; /* Set if the quality bar should be displayed. */ int (*check_cb)(struct pin_entry_info_s *); /* CB used to check the PIN */ void *check_cb_arg; /* optional argument which might be of use in the CB */ const char *cb_errtext; /* used by the cb to display a specific error */ size_t max_length; /* Allocated length of the buffer PIN. */ char pin[1]; /* The buffer to hold the PIN or passphrase. It's actual allocated length is given by MAX_LENGTH (above). */ }; /* Types of the private keys. */ enum { PRIVATE_KEY_UNKNOWN = 0, /* Type of key is not known. */ PRIVATE_KEY_CLEAR = 1, /* The key is not protected. */ PRIVATE_KEY_PROTECTED = 2, /* The key is protected. */ PRIVATE_KEY_SHADOWED = 3, /* The key is a stub for a smartcard based key. */ PROTECTED_SHARED_SECRET = 4 /* RFU. */ }; /* Values for the cache_mode arguments. */ typedef enum { CACHE_MODE_IGNORE = 0, /* Special mode to bypass the cache. */ CACHE_MODE_ANY, /* Any mode except ignore matches. */ CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */ CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */ CACHE_MODE_SSH, /* SSH related cache. */ CACHE_MODE_NONCE /* This is a non-predictable nonce. */ } cache_mode_t; /* The TTL is seconds used for adding a new nonce mode cache item. */ #define CACHE_TTL_NONCE 120 /* The TTL in seconds used by the --preset option of some commands. This is the default value changeable by an OPTION command. */ #define CACHE_TTL_OPT_PRESET 900 /* The type of a function to lookup a TTL by a keygrip. */ typedef int (*lookup_ttl_t)(const char *hexgrip); /*-- gpg-agent.c --*/ void agent_exit (int rc) JNLIB_GCC_A_NR; /* Also implemented in other tools */ const char *get_agent_socket_name (void); const char *get_agent_ssh_socket_name (void); #ifdef HAVE_W32_SYSTEM void *get_agent_scd_notify_event (void); #endif void agent_sighup_action (void); int map_pk_openpgp_to_gcry (int openpgp_algo); /*-- command.c --*/ gpg_error_t agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid); gpg_error_t agent_write_status (ctrl_t ctrl, const char *keyword, ...) GNUPG_GCC_A_SENTINEL(0); gpg_error_t agent_print_status (ctrl_t ctrl, const char *keyword, const char *format, ...) JNLIB_GCC_A_PRINTF(3,4); void bump_key_eventcounter (void); void bump_card_eventcounter (void); void start_command_handler (ctrl_t, gnupg_fd_t, gnupg_fd_t); gpg_error_t pinentry_loopback (ctrl_t, const char *keyword, unsigned char **buffer, size_t *size, size_t max_length); #ifdef HAVE_W32_SYSTEM int serve_mmapped_ssh_request (ctrl_t ctrl, unsigned char *request, size_t maxreqlen); #endif /*HAVE_W32_SYSTEM*/ /*-- command-ssh.c --*/ ssh_control_file_t ssh_open_control_file (void); void ssh_close_control_file (ssh_control_file_t cf); gpg_error_t ssh_read_control_file (ssh_control_file_t cf, char *r_hexgrip, int *r_disabled, int *r_ttl, int *r_confirm); gpg_error_t ssh_search_control_file (ssh_control_file_t cf, const char *hexgrip, int *r_disabled, int *r_ttl, int *r_confirm); void start_command_handler_ssh (ctrl_t, gnupg_fd_t); /*-- findkey.c --*/ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force); gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, gcry_sexp_t *result, char **r_passphrase); gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); int agent_is_dsa_key (gcry_sexp_t s_key); int agent_is_eddsa_key (gcry_sexp_t s_key); int agent_key_available (const unsigned char *grip); gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, unsigned char **r_shadow_info); gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text, const unsigned char *grip); /*-- call-pinentry.c --*/ void initialize_module_call_pinentry (void); void agent_query_dump_state (void); void agent_reset_query (ctrl_t ctrl); int pinentry_active_p (ctrl_t ctrl, int waitseconds); int agent_askpin (ctrl_t ctrl, const char *desc_text, const char *prompt_text, const char *inital_errtext, struct pin_entry_info_s *pininfo); int agent_get_passphrase (ctrl_t ctrl, char **retpass, const char *desc, const char *prompt, const char *errtext, int with_qualitybar); int agent_get_confirmation (ctrl_t ctrl, const char *desc, const char *ok, const char *notokay, int with_cancel); int agent_show_message (ctrl_t ctrl, const char *desc, const char *ok_btn); int agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn); void agent_popup_message_stop (ctrl_t ctrl); /*-- cache.c --*/ void initialize_module_cache (void); void deinitialize_module_cache (void); void agent_flush_cache (void); int agent_put_cache (const char *key, cache_mode_t cache_mode, const char *data, int ttl); char *agent_get_cache (const char *key, cache_mode_t cache_mode); +void agent_store_cache_hit (const char *key); /*-- pksign.c --*/ int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, gcry_sexp_t *signature_sexp, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, const void *overridedata, size_t overridedatalen); int agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, membuf_t *outbuf, cache_mode_t cache_mode); /*-- pkdecrypt.c --*/ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, const unsigned char *ciphertext, size_t ciphertextlen, membuf_t *outbuf, int *r_padding); /*-- genkey.c --*/ int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent); gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, char **r_passphrase); int agent_genkey (ctrl_t ctrl, const char *cache_nonce, const char *keyparam, size_t keyparmlen, int no_protection, int preset, membuf_t *outbuf); gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, char **passphrase_addr); /*-- protect.c --*/ unsigned long get_standard_s2k_count (void); unsigned char get_standard_s2k_count_rfc4880 (void); int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen, unsigned long s2k_count); int agent_unprotect (ctrl_t ctrl, const unsigned char *protectedkey, const char *passphrase, gnupg_isotime_t protected_at, unsigned char **result, size_t *resultlen); int agent_private_key_type (const unsigned char *privatekey); unsigned char *make_shadow_info (const char *serialno, const char *idstring); int agent_shadow_key (const unsigned char *pubkey, const unsigned char *shadow_info, unsigned char **result); int agent_get_shadow_info (const unsigned char *shadowkey, unsigned char const **shadow_info); gpg_error_t parse_shadow_info (const unsigned char *shadow_info, char **r_hexsn, char **r_idstr, int *r_pinlen); gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo, int s2kmode, const unsigned char *s2ksalt, unsigned int s2kcount, unsigned char *key, size_t keylen); /*-- trustlist.c --*/ void initialize_module_trustlist (void); gpg_error_t agent_istrusted (ctrl_t ctrl, const char *fpr, int *r_disabled); gpg_error_t agent_listtrusted (void *assuan_context); gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag); void agent_reload_trustlist (void); /*-- divert-scd.c --*/ int divert_pksign (ctrl_t ctrl, const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig, size_t *r_siglen); int divert_pkdecrypt (ctrl_t ctrl, const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len, int *r_padding); int divert_generic_cmd (ctrl_t ctrl, const char *cmdline, void *assuan_context); int divert_writekey (ctrl_t ctrl, int force, const char *serialno, const char *id, const char *keydata, size_t keydatalen); /*-- call-scd.c --*/ void initialize_module_call_scd (void); void agent_scd_dump_state (void); int agent_scd_check_running (void); void agent_scd_check_aliveness (void); int agent_reset_scd (ctrl_t ctrl); int agent_card_learn (ctrl_t ctrl, void (*kpinfo_cb)(void*, const char *), void *kpinfo_cb_arg, void (*certinfo_cb)(void*, const char *), void *certinfo_cb_arg, void (*sinfo_cb)(void*, const char *, size_t, const char *), void *sinfo_cb_arg); int agent_card_serialno (ctrl_t ctrl, char **r_serialno); int agent_card_pksign (ctrl_t ctrl, const char *keyid, int (*getpin_cb)(void *, const char *, char*, size_t), void *getpin_cb_arg, int mdalgo, const unsigned char *indata, size_t indatalen, unsigned char **r_buf, size_t *r_buflen); int agent_card_pkdecrypt (ctrl_t ctrl, const char *keyid, int (*getpin_cb)(void *, const char *, char*,size_t), void *getpin_cb_arg, 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, unsigned char **r_buf); int agent_card_writekey (ctrl_t ctrl, int force, const char *serialno, const char *id, const char *keydata, size_t keydatalen, int (*getpin_cb)(void *, const char *, char*, size_t), void *getpin_cb_arg); gpg_error_t agent_card_getattr (ctrl_t ctrl, const char *name, char **result); int agent_card_scd (ctrl_t ctrl, const char *cmdline, int (*getpin_cb)(void *, const char *, char*, size_t), void *getpin_cb_arg, void *assuan_context); /*-- learncard.c --*/ int agent_handle_learn (ctrl_t ctrl, void *assuan_context); #endif /*AGENT_H*/ diff --git a/agent/cache.c b/agent/cache.c index d4deaeb8c..49402e434 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -1,444 +1,471 @@ /* cache.c - keep a cache of passphrases * Copyright (C) 2002, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "agent.h" /* The size of the encryption key in bytes. */ #define ENCRYPTION_KEYSIZE (128/8) /* A mutex used to protect the encryption. This is required because we use one context to do all encryption and decryption. */ static npth_mutex_t encryption_lock; /* The encryption context. This is the only place where the encryption key for all cached entries is available. It would be nice to keep this (or just the key) in some hardware device, for example a TPM. Libgcrypt could be extended to provide such a service. With the current scheme it is easy to retrieve the cached entries if access to Libgcrypt's memory is available. The encryption merely avoids grepping for clear texts in the memory. Nevertheless the encryption provides the necessary infrastructure to make it more secure. */ static gcry_cipher_hd_t encryption_handle; struct secret_data_s { int totallen; /* This includes the padding and space for AESWRAP. */ char data[1]; /* A string. */ }; typedef struct cache_item_s *ITEM; struct cache_item_s { ITEM next; time_t created; time_t accessed; int ttl; /* max. lifetime given in seconds, -1 one means infinite */ struct secret_data_s *pw; cache_mode_t cache_mode; char key[1]; }; /* The cache himself. */ static ITEM thecache; +/* NULL or the last cache key stored by agent_store_cache_hit. */ +static char *last_stored_cache_key; + /* This function must be called once to initialize this module. It has to be done before a second thread is spawned. */ void initialize_module_cache (void) { int err; err = npth_mutex_init (&encryption_lock, NULL); if (err) log_fatal ("error initializing cache module: %s\n", strerror (err)); } void deinitialize_module_cache (void) { gcry_cipher_close (encryption_handle); encryption_handle = NULL; } /* We do the encryption init on the fly. We can't do it in the module init code because that is run before we listen for connections and in case we are started on demand by gpg etc. it will only wait for a few seconds to decide whether the agent may now accept connections. Thus we should get into listen state as soon as possible. */ static gpg_error_t init_encryption (void) { gpg_error_t err; void *key; int res; if (encryption_handle) return 0; /* Shortcut - Already initialized. */ res = npth_mutex_lock (&encryption_lock); if (res) log_fatal ("failed to acquire cache encryption mutex: %s\n", strerror (res)); err = gcry_cipher_open (&encryption_handle, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, GCRY_CIPHER_SECURE); if (!err) { key = gcry_random_bytes (ENCRYPTION_KEYSIZE, GCRY_STRONG_RANDOM); if (!key) err = gpg_error_from_syserror (); else { err = gcry_cipher_setkey (encryption_handle, key, ENCRYPTION_KEYSIZE); xfree (key); } if (err) { gcry_cipher_close (encryption_handle); encryption_handle = NULL; } } if (err) log_error ("error initializing cache encryption context: %s\n", gpg_strerror (err)); res = npth_mutex_unlock (&encryption_lock); if (res) log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res)); return err? gpg_error (GPG_ERR_NOT_INITIALIZED) : 0; } static void release_data (struct secret_data_s *data) { xfree (data); } static gpg_error_t new_data (const char *string, struct secret_data_s **r_data) { gpg_error_t err; struct secret_data_s *d, *d_enc; size_t length; int total; int res; *r_data = NULL; err = init_encryption (); if (err) return err; length = strlen (string) + 1; /* We pad the data to 32 bytes so that it get more complicated finding something out by watching allocation patterns. This is usally not possible but we better assume nothing about our secure storage provider. To support the AESWRAP mode we need to add 8 extra bytes as well. */ total = (length + 8) + 32 - ((length+8) % 32); d = xtrymalloc_secure (sizeof *d + total - 1); if (!d) return gpg_error_from_syserror (); memcpy (d->data, string, length); d_enc = xtrymalloc (sizeof *d_enc + total - 1); if (!d_enc) { err = gpg_error_from_syserror (); xfree (d); return err; } d_enc->totallen = total; res = npth_mutex_lock (&encryption_lock); if (res) log_fatal ("failed to acquire cache encryption mutex: %s\n", strerror (res)); err = gcry_cipher_encrypt (encryption_handle, d_enc->data, total, d->data, total - 8); xfree (d); res = npth_mutex_unlock (&encryption_lock); if (res) log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res)); if (err) { xfree (d_enc); return err; } *r_data = d_enc; return 0; } /* Check whether there are items to expire. */ static void housekeeping (void) { ITEM r, rprev; time_t current = gnupg_get_time (); /* First expire the actual data */ for (r=thecache; r; r = r->next) { if (r->pw && r->ttl >= 0 && r->accessed + r->ttl < current) { if (DBG_CACHE) log_debug (" expired '%s' (%ds after last access)\n", r->key, r->ttl); release_data (r->pw); r->pw = NULL; r->accessed = current; } } /* 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. */ for (r=thecache; r; r = r->next) { unsigned long maxttl; switch (r->cache_mode) { 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' (%lus after creation)\n", r->key, opt.max_cache_ttl); release_data (r->pw); r->pw = NULL; r->accessed = current; } } /* 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; ) { if (!r->pw && r->ttl >= 0 && r->accessed + 60*30 < current) { ITEM r2 = r->next; if (DBG_CACHE) log_debug (" removed '%s' (mode %d) (slot not used for 30m)\n", r->key, r->cache_mode); xfree (r); if (!rprev) thecache = r2; else rprev->next = r2; r = r2; } else { rprev = r; r = r->next; } } } void agent_flush_cache (void) { ITEM r; if (DBG_CACHE) log_debug ("agent_flush_cache\n"); for (r=thecache; r; r = r->next) { if (r->pw) { if (DBG_CACHE) log_debug (" flushing '%s'\n", r->key); release_data (r->pw); r->pw = NULL; r->accessed = 0; } } } /* Store the string DATA in the cache under KEY and mark it with a maximum lifetime of TTL seconds. If there is already data under this key, it will be replaced. Using a DATA of NULL deletes the entry. A TTL of 0 is replaced by the default TTL and a TTL of -1 set infinite timeout. CACHE_MODE is stored with the cache entry and used to select different timeouts. */ int agent_put_cache (const char *key, cache_mode_t cache_mode, const char *data, int ttl) { gpg_error_t err = 0; ITEM r; if (DBG_CACHE) log_debug ("agent_put_cache '%s' (mode %d) requested ttl=%d\n", key, cache_mode, ttl); housekeeping (); if (!ttl) { switch(cache_mode) { case CACHE_MODE_SSH: ttl = opt.def_cache_ttl_ssh; break; default: ttl = opt.def_cache_ttl; break; } } if ((!ttl && data) || cache_mode == CACHE_MODE_IGNORE) return 0; for (r=thecache; r; r = r->next) { if (((cache_mode != CACHE_MODE_USER && cache_mode != CACHE_MODE_NONCE) || r->cache_mode == cache_mode) && !strcmp (r->key, key)) break; } if (r) /* Replace. */ { if (r->pw) { release_data (r->pw); r->pw = NULL; } if (data) { r->created = r->accessed = gnupg_get_time (); r->ttl = ttl; r->cache_mode = cache_mode; err = new_data (data, &r->pw); if (err) log_error ("error replacing cache item: %s\n", gpg_strerror (err)); } } else if (data) /* Insert. */ { r = xtrycalloc (1, sizeof *r + strlen (key)); if (!r) err = gpg_error_from_syserror (); else { strcpy (r->key, key); r->created = r->accessed = gnupg_get_time (); r->ttl = ttl; r->cache_mode = cache_mode; err = new_data (data, &r->pw); if (err) xfree (r); else { r->next = thecache; thecache = r; } } if (err) log_error ("error inserting cache item: %s\n", gpg_strerror (err)); } return err; } /* Try to find an item in the cache. Note that we currently don't make use of CACHE_MODE except for CACHE_MODE_NONCE and CACHE_MODE_USER. */ char * agent_get_cache (const char *key, cache_mode_t cache_mode) { gpg_error_t err; ITEM r; char *value = NULL; int res; + int last_stored = 0; if (cache_mode == CACHE_MODE_IGNORE) return NULL; + if (!key) + { + key = last_stored_cache_key; + if (!key) + return NULL; + last_stored = 1; + } + + if (DBG_CACHE) - log_debug ("agent_get_cache '%s' (mode %d) ...\n", key, cache_mode); + log_debug ("agent_get_cache '%s' (mode %d)%s ...\n", + key, cache_mode, + last_stored? " (stored cache key)":""); housekeeping (); for (r=thecache; r; r = r->next) { if (r->pw && ((cache_mode != CACHE_MODE_USER && cache_mode != CACHE_MODE_NONCE) || r->cache_mode == cache_mode) && !strcmp (r->key, key)) { + /* Note: To avoid races KEY may not be accessed anymore below. */ r->accessed = gnupg_get_time (); if (DBG_CACHE) log_debug ("... hit\n"); if (r->pw->totallen < 32) err = gpg_error (GPG_ERR_INV_LENGTH); else if ((err = init_encryption ())) ; else if (!(value = xtrymalloc_secure (r->pw->totallen - 8))) err = gpg_error_from_syserror (); else { res = npth_mutex_lock (&encryption_lock); if (res) log_fatal ("failed to acquire cache encryption mutex: %s\n", strerror (res)); err = gcry_cipher_decrypt (encryption_handle, value, r->pw->totallen - 8, r->pw->data, r->pw->totallen); res = npth_mutex_unlock (&encryption_lock); if (res) log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res)); } if (err) { xfree (value); value = NULL; log_error ("retrieving cache entry '%s' failed: %s\n", key, gpg_strerror (err)); } return value; } } if (DBG_CACHE) log_debug ("... miss\n"); return NULL; } + + +/* Store the key for the last successful cache hit. That value is + used by agent_get_cache if the requested KEY is given as NULL. + NULL may be used to remove that key. */ +void +agent_store_cache_hit (const char *key) +{ + xfree (last_stored_cache_key); + last_stored_cache_key = key? xtrystrdup (key) : NULL; +} diff --git a/agent/findkey.c b/agent/findkey.c index 5ff263ef4..fbe303116 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1,1294 +1,1336 @@ /* findkey.c - Locate the secret key * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include /* (we use pth_sleep) */ #include "agent.h" #include "i18n.h" #include "../common/ssh-utils.h" #ifndef O_BINARY #define O_BINARY 0 #endif /* Helper to pass data to the check callback of the unprotect function. */ struct try_unprotect_arg_s { ctrl_t ctrl; const unsigned char *protected_key; unsigned char *unprotected_key; int change_required; /* Set by the callback to indicate that the user should chnage the passphrase. */ }; /* Write an S-expression formatted key to our key storage. With FORCE passed as true an existing key with the given GRIP will get overwritten. */ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force) { char *fname; estream_t fp; char hexgrip[40+4+1]; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); /* FIXME: Write to a temp file first so that write failures during key updates won't lead to a key loss. */ if (!force && !access (fname, F_OK)) { log_error ("secret key file '%s' already exists\n", fname); xfree (fname); return gpg_error (GPG_ERR_EEXIST); } fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); xfree (fname); return tmperr; } if (es_fwrite (buffer, length, 1, fp) != 1) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); es_fclose (fp); gnupg_remove (fname); xfree (fname); return tmperr; } if (es_fclose (fp)) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); gnupg_remove (fname); xfree (fname); return tmperr; } bump_key_eventcounter (); xfree (fname); return 0; } /* Callback function to try the unprotection from the passphrase query code. */ static int try_unprotect_cb (struct pin_entry_info_s *pi) { struct try_unprotect_arg_s *arg = pi->check_cb_arg; size_t dummy; gpg_error_t err; gnupg_isotime_t now, protected_at, tmptime; char *desc = NULL; assert (!arg->unprotected_key); arg->change_required = 0; err = agent_unprotect (arg->ctrl, arg->protected_key, pi->pin, protected_at, &arg->unprotected_key, &dummy); if (err) return err; if (!opt.max_passphrase_days || arg->ctrl->in_passwd) return 0; /* No regular passphrase change required. */ if (!*protected_at) { /* No protection date known - must force passphrase change. */ desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A" "Please change it now.")); if (!desc) return gpg_error_from_syserror (); } else { gnupg_get_isotime (now); gnupg_copy_time (tmptime, protected_at); err = add_days_to_isotime (tmptime, opt.max_passphrase_days); if (err) return err; if (strcmp (now, tmptime) > 0 ) { /* Passphrase "expired". */ desc = xtryasprintf (_("This passphrase has not been changed%%0A" "since %.4s-%.2s-%.2s. Please change it now."), protected_at, protected_at+4, protected_at+6); if (!desc) return gpg_error_from_syserror (); } } if (desc) { /* Change required. */ if (opt.enforce_passphrase_constraints) { err = agent_get_confirmation (arg->ctrl, desc, _("Change passphrase"), NULL, 0); if (!err) arg->change_required = 1; } else { err = agent_get_confirmation (arg->ctrl, desc, _("Change passphrase"), _("I'll change it later"), 0); if (!err) arg->change_required = 1; else if (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) err = 0; } xfree (desc); } return 0; } /* Modify a Key description, replacing certain special format characters. List of currently supported replacements: %% - Replaced by a single % %c - Replaced by the content of COMMENT. %C - Same as %c but put into parentheses. %F - Replaced by an ssh style fingerprint computed from KEY. The functions returns 0 on success or an error code. On success a newly allocated string is stored at the address of RESULT. */ static gpg_error_t modify_description (const char *in, const char *comment, const gcry_sexp_t key, char **result) { size_t comment_length; size_t in_len; size_t out_len; char *out; size_t i; int special, pass; char *ssh_fpr = NULL; comment_length = strlen (comment); in_len = strlen (in); /* First pass calculates the length, second pass does the actual copying. */ out = NULL; out_len = 0; for (pass=0; pass < 2; pass++) { special = 0; for (i = 0; i < in_len; i++) { if (special) { special = 0; switch (in[i]) { case '%': if (out) *out++ = '%'; else out_len++; break; case 'c': /* Comment. */ if (out) { memcpy (out, comment, comment_length); out += comment_length; } else out_len += comment_length; break; case 'C': /* Comment. */ if (!comment_length) ; else if (out) { *out++ = '('; memcpy (out, comment, comment_length); out += comment_length; *out++ = ')'; } else out_len += comment_length + 2; break; case 'F': /* SSH style fingerprint. */ if (!ssh_fpr && key) ssh_get_fingerprint_string (key, &ssh_fpr); if (ssh_fpr) { if (out) out = stpcpy (out, ssh_fpr); else out_len += strlen (ssh_fpr); } break; default: /* Invalid special sequences are kept as they are. */ if (out) { *out++ = '%'; *out++ = in[i]; } else out_len+=2; break; } } else if (in[i] == '%') special = 1; else { if (out) *out++ = in[i]; else out_len++; } } if (!pass) { *result = out = xtrymalloc (out_len + 1); if (!out) { xfree (ssh_fpr); return gpg_error_from_syserror (); } } } *out = 0; assert (*result + out_len == out); xfree (ssh_fpr); return 0; } /* Unprotect the canconical encoded S-expression key in KEYBUF. GRIP should be the hex encoded keygrip of that key to be used with the caching mechanism. DESC_TEXT may be set to override the default description used for the pinentry. If LOOKUP_TTL is given this function is used to lookup the default ttl. If R_PASSPHRASE is not NULL, the function succeeded and the key was protected the used passphrase (entered or from the cache) is stored there; if not NULL will be stored. The caller needs to free the returned passphrase. */ static int unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, unsigned char **keybuf, const unsigned char *grip, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, char **r_passphrase) { struct pin_entry_info_s *pi; struct try_unprotect_arg_s arg; int rc; unsigned char *result; size_t resultlen; char hexgrip[40+1]; if (r_passphrase) *r_passphrase = NULL; bin2hex (grip, 20, hexgrip); /* Initially try to get it using a cache nonce. */ if (cache_nonce) { char *pw; pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE); if (pw) { rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen); if (!rc) { if (r_passphrase) *r_passphrase = pw; else xfree (pw); xfree (*keybuf); *keybuf = result; return 0; } xfree (pw); } } /* First try to get it from the cache - if there is none or we can't unprotect it, we fall back to ask the user */ if (cache_mode != CACHE_MODE_IGNORE) { char *pw; retry: pw = agent_get_cache (hexgrip, cache_mode); if (pw) { rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen); if (!rc) { + if (cache_mode == CACHE_MODE_NORMAL) + agent_store_cache_hit (hexgrip); if (r_passphrase) *r_passphrase = pw; else xfree (pw); xfree (*keybuf); *keybuf = result; return 0; } xfree (pw); rc = 0; } + else if (cache_mode == CACHE_MODE_NORMAL) + { + /* The standard use of GPG keys is to have a signing and an + encryption subkey. Commonly both use the same + passphrase. We try to help the user to enter the + passphrase only once by silently trying the last + correctly entered passphrase. Checking one additional + passphrase should be acceptable; despite the S2K + introduced delays. The assumed workflow is: + + 1. Read encrypted message in a MUA and thus enter a + passphrase for the encryption subkey. + + 2. Reply to that mail with an encrypted and signed + mail, thus entering the passphrase for the signing + subkey. + + We can often avoid the passphrase entry in the second + step. We do this only in normal mode, so not to + interfere with unrelated cache entries. */ + pw = agent_get_cache (NULL, cache_mode); + if (pw) + { + rc = agent_unprotect (ctrl, *keybuf, pw, NULL, + &result, &resultlen); + if (!rc) + { + if (r_passphrase) + *r_passphrase = pw; + else + xfree (pw); + xfree (*keybuf); + *keybuf = result; + return 0; + } + xfree (pw); + rc = 0; + } + } /* If the pinentry is currently in use, we wait up to 60 seconds for it to close and check the cache again. This solves a common situation where several requests for unprotecting a key have been made but the user is still entering the passphrase for the first request. Because all requests to agent_askpin are serialized they would then pop up one after the other to request the passphrase - despite that the user has already entered it and is then available in the cache. This implementation is not race free but in the worst case the user has to enter the passphrase only once more. */ if (pinentry_active_p (ctrl, 0)) { /* Active - wait */ if (!pinentry_active_p (ctrl, 60)) { /* We need to give the other thread a chance to actually put it into the cache. */ npth_sleep (1); goto retry; } /* Timeout - better call pinentry now the plain way. */ } } pi = gcry_calloc_secure (1, sizeof (*pi) + 100); if (!pi) return gpg_error_from_syserror (); pi->max_length = 100; pi->min_digits = 0; /* we want a real passphrase */ pi->max_digits = 16; pi->max_tries = 3; pi->check_cb = try_unprotect_cb; arg.ctrl = ctrl; arg.protected_key = *keybuf; arg.unprotected_key = NULL; arg.change_required = 0; pi->check_cb_arg = &arg; rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi); if (!rc) { assert (arg.unprotected_key); if (arg.change_required) { size_t canlen, erroff; gcry_sexp_t s_skey; assert (arg.unprotected_key); canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)arg.unprotected_key, canlen); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); wipememory (arg.unprotected_key, canlen); xfree (arg.unprotected_key); xfree (pi); return rc; } rc = agent_protect_and_store (ctrl, s_skey, NULL); gcry_sexp_release (s_skey); if (rc) { log_error ("changing the passphrase failed: %s\n", gpg_strerror (rc)); wipememory (arg.unprotected_key, canlen); xfree (arg.unprotected_key); xfree (pi); return rc; } } else { agent_put_cache (hexgrip, cache_mode, pi->pin, lookup_ttl? lookup_ttl (hexgrip) : 0); + agent_store_cache_hit (hexgrip); if (r_passphrase && *pi->pin) *r_passphrase = xtrystrdup (pi->pin); } xfree (*keybuf); *keybuf = arg.unprotected_key; } xfree (pi); return rc; } /* Read the key identified by GRIP from the private key directory and return it as an gcrypt S-expression object in RESULT. On failure returns an error code and stores NULL at RESULT. */ static gpg_error_t read_key_file (const unsigned char *grip, gcry_sexp_t *result) { int rc; char *fname; estream_t fp; struct stat st; unsigned char *buf; size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; *result = NULL; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); fp = es_fopen (fname, "rb"); if (!fp) { rc = gpg_error_from_syserror (); if (gpg_err_code (rc) != GPG_ERR_ENOENT) log_error ("can't open '%s': %s\n", fname, strerror (errno)); xfree (fname); return rc; } if (fstat (es_fileno (fp), &st)) { rc = gpg_error_from_syserror (); log_error ("can't stat '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } buflen = st.st_size; buf = xtrymalloc (buflen+1); if (!buf) { rc = gpg_error_from_syserror (); log_error ("error allocating %zu bytes for '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } if (es_fread (buf, buflen, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading %zu bytes from '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } /* Convert the file into a gcrypt S-expression object. */ rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); xfree (fname); es_fclose (fp); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); return rc; } *result = s_skey; return 0; } /* Remove the key identified by GRIP from the private key directory. */ static gpg_error_t remove_key_file (const unsigned char *grip) { gpg_error_t err = 0; char *fname; char hexgrip[40+4+1]; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); if (gnupg_remove (fname)) err = gpg_error_from_syserror (); xfree (fname); return err; } /* Return the secret key as an S-Exp in RESULT after locating it using the GRIP. If the operation shall be diverted to a token, an allocated S-expression with the shadow_info part from the file is stored at SHADOW_INFO; if not NULL will be stored at SHADOW_INFO. CACHE_MODE defines now the cache shall be used. DESC_TEXT may be set to present a custom description for the pinentry. LOOKUP_TTL is an optional function to convey a TTL to the cache manager; we do not simply pass the TTL value because the value is only needed if an unprotect action was needed and looking up the TTL may have some overhead (e.g. scanning the sshcontrol file). If a CACHE_NONCE is given that cache item is first tried to get a passphrase. If R_PASSPHRASE is not NULL, the function succeeded and the key was protected the used passphrase (entered or from the cache) is stored there; if not NULL will be stored. The caller needs to free the returned passphrase. */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, gcry_sexp_t *result, char **r_passphrase) { int rc; unsigned char *buf; size_t len, buflen, erroff; gcry_sexp_t s_skey; *result = NULL; if (shadow_info) *shadow_info = NULL; if (r_passphrase) *r_passphrase = NULL; rc = read_key_file (grip, &s_skey); if (rc) { if (gpg_err_code (rc) == GPG_ERR_ENOENT) rc = gpg_error (GPG_ERR_NO_SECKEY); return rc; } /* For use with the protection functions we also need the key as an canonical encoded S-expression in a buffer. Create this buffer now. */ rc = make_canon_sexp (s_skey, &buf, &len); if (rc) return rc; switch (agent_private_key_type (buf)) { case PRIVATE_KEY_CLEAR: break; /* no unprotection needed */ case PRIVATE_KEY_PROTECTED: { char *desc_text_final; char *comment = NULL; /* Note, that we will take the comment as a C string for display purposes; i.e. all stuff beyond a Nul character is ignored. */ { gcry_sexp_t comment_sexp; comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); if (comment_sexp) comment = gcry_sexp_nth_string (comment_sexp, 1); gcry_sexp_release (comment_sexp); } desc_text_final = NULL; if (desc_text) rc = modify_description (desc_text, comment? comment:"", s_skey, &desc_text_final); gcry_free (comment); if (!rc) { rc = unprotect (ctrl, cache_nonce, desc_text_final, &buf, grip, cache_mode, lookup_ttl, r_passphrase); if (rc) log_error ("failed to unprotect the secret key: %s\n", gpg_strerror (rc)); } xfree (desc_text_final); } break; case PRIVATE_KEY_SHADOWED: if (shadow_info) { const unsigned char *s; size_t n; rc = agent_get_shadow_info (buf, &s); if (!rc) { n = gcry_sexp_canon_len (s, 0, NULL,NULL); assert (n); *shadow_info = xtrymalloc (n); if (!*shadow_info) rc = out_of_core (); else { memcpy (*shadow_info, s, n); rc = 0; } } if (rc) log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); } else rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY); break; default: log_error ("invalid private key format\n"); rc = gpg_error (GPG_ERR_BAD_SECKEY); break; } gcry_sexp_release (s_skey); s_skey = NULL; if (rc) { xfree (buf); if (r_passphrase) { xfree (*r_passphrase); *r_passphrase = NULL; } return rc; } buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); wipememory (buf, buflen); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); if (r_passphrase) { xfree (*r_passphrase); *r_passphrase = NULL; } return rc; } *result = s_skey; return 0; } /* Return the string name from the S-expression S_KEY as well as a string describing the names of the parameters. ALGONAMESIZE and ELEMSSIZE give the allocated size of the provided buffers. The buffers may be NULL if not required. If R_LIST is not NULL the top level list will be stored there; the caller needs to release it in this case. */ static gpg_error_t key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, char *r_algoname, size_t algonamesize, char *r_elems, size_t elemssize) { gcry_sexp_t list, l2; const char *name, *algoname, *elems; size_t n; if (r_list) *r_list = NULL; list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_key, "protected-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_key, "private-key", 0 ); if (!list) { log_error ("invalid private key format\n"); return gpg_error (GPG_ERR_BAD_SECKEY); } l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if (n==3 && !memcmp (name, "rsa", 3)) { algoname = "rsa"; elems = "ne"; } else if (n==3 && !memcmp (name, "dsa", 3)) { algoname = "dsa"; elems = "pqgy"; } else if (n==3 && !memcmp (name, "ecc", 3)) { algoname = "ecc"; elems = "pabgnq"; } else if (n==5 && !memcmp (name, "ecdsa", 5)) { algoname = "ecdsa"; elems = "pabgnq"; } else if (n==4 && !memcmp (name, "ecdh", 4)) { algoname = "ecdh"; elems = "pabgnq"; } else if (n==3 && !memcmp (name, "elg", 3)) { algoname = "elg"; elems = "pgy"; } else { log_error ("unknown private key algorithm\n"); gcry_sexp_release (list); return gpg_error (GPG_ERR_BAD_SECKEY); } if (r_algoname) { if (strlen (algoname) >= algonamesize) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); strcpy (r_algoname, algoname); } if (r_elems) { if (strlen (elems) >= elemssize) return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); strcpy (r_elems, elems); } if (r_list) *r_list = list; else gcry_sexp_release (list); return 0; } /* Return true if KEYPARMS holds an EdDSA key. */ static int is_eddsa (gcry_sexp_t keyparms) { int result = 0; gcry_sexp_t list; const char *s; size_t n; int i; list = gcry_sexp_find_token (keyparms, "flags", 0); for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) { s = gcry_sexp_nth_data (list, i, &n); if (!s) continue; /* Not a data element. */ if (n == 5 && !memcmp (s, "eddsa", 5)) { result = 1; break; } } gcry_sexp_release (list); return result; } /* Return the public key algorithm number if S_KEY is a DSA style key. If it is not a DSA style key, return 0. */ int agent_is_dsa_key (gcry_sexp_t s_key) { int result; gcry_sexp_t list; char algoname[6]; if (!s_key) return 0; if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) return 0; /* Error - assume it is not an DSA key. */ if (!strcmp (algoname, "dsa")) result = GCRY_PK_DSA; else if (!strcmp (algoname, "ecc")) { if (is_eddsa (list)) result = 0; else result = GCRY_PK_ECDSA; } else if (!strcmp (algoname, "ecdsa")) result = GCRY_PK_ECDSA; else result = 0; gcry_sexp_release (list); return result; } /* Return true if S_KEY is an EdDSA key as used with curve Ed25519. */ int agent_is_eddsa_key (gcry_sexp_t s_key) { int result; gcry_sexp_t list; char algoname[6]; if (!s_key) return 0; if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) return 0; /* Error - assume it is not an EdDSA key. */ if (!strcmp (algoname, "ecc") && is_eddsa (list)) result = 1; else if (!strcmp (algoname, "eddsa")) /* backward compatibility. */ result = 1; else result = 0; gcry_sexp_release (list); return result; } /* Return the key for the keygrip GRIP. The result is stored at RESULT. This function extracts the key from the private key database and returns it as an S-expression object as it is. On failure an error code is returned and NULL stored at RESULT. */ gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result) { gpg_error_t err; gcry_sexp_t s_skey; (void)ctrl; *result = NULL; err = read_key_file (grip, &s_skey); if (!err) *result = s_skey; return err; } /* Return the public key for the keygrip GRIP. The result is stored at RESULT. This function extracts the public key from the private key database. On failure an error code is returned and NULL stored at RESULT. */ gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result) { gpg_error_t err; int i, idx; gcry_sexp_t s_skey; char algoname[6]; char elems[7]; gcry_sexp_t uri_sexp, comment_sexp; const char *uri, *comment; size_t uri_length, comment_length; char *format, *p; void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2 for comment + end-of-list. */ int argidx; gcry_sexp_t list, l2; const char *s; gcry_mpi_t *array; (void)ctrl; *result = NULL; err = read_key_file (grip, &s_skey); if (err) return err; err = key_parms_from_sexp (s_skey, &list, algoname, sizeof algoname, elems, sizeof elems); if (err) { gcry_sexp_release (s_skey); return err; } /* Allocate an array for the parameters and copy them out of the secret key. FIXME: We should have a generic copy function. */ array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { err = gpg_error_from_syserror (); gcry_sexp_release (list); gcry_sexp_release (s_skey); return err; } for (idx=0, s=elems; *s; s++, idx++ ) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { /* Required parameter not found. */ for (i=0; i. */ #include #include #include #include #include #include #include #include #include #include #include "gpg.h" #include "util.h" #include "main.h" #include "packet.h" #include "ttyio.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "status.h" #include "i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "pkglue.h" /* The default algorithms. If you change them remember to change them also in gpg.c:gpgconf_list. You should also check that the value is inside the bounds enforced by ask_keysize and gen_xxx. */ #define DEFAULT_STD_ALGO GCRY_PK_RSA #define DEFAULT_STD_KEYSIZE 2048 /* Flag bits used during key generation. */ #define KEYGEN_FLAG_NO_PROTECTION 1 #define KEYGEN_FLAG_TRANSIENT_KEY 2 /* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 enum para_name { pKEYTYPE, pKEYLENGTH, pKEYCURVE, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, pSUBKEYCURVE, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, pPREFERENCES, pREVOKER, pUSERID, pCREATIONDATE, pKEYCREATIONDATE, /* Same in seconds since epoch. */ pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ pSUBKEYEXPIRE, /* in n seconds */ pPASSPHRASE, pPASSPHRASE_DEK, pPASSPHRASE_S2K, pSERIALNO, pCARDBACKUPKEY, pHANDLE, pKEYSERVER }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { DEK *dek; STRING2KEY *s2k; u32 expire; u32 creation; unsigned int usage; struct revocation_key revkey; char value[1]; } u; }; struct output_control_s { int lnr; int dryrun; int ask_passphrase; unsigned int keygen_flags; int use_files; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t *afx; } pub; }; struct opaque_data_usage_and_pk { unsigned int usage; PKT_public_key *pk; }; static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static int mdc_available,ks_modify; static void do_generate_keypair( struct para_data_s *para, struct output_control_s *outctrl, int card ); static int write_keyblock (iobuf_t out, kbnode_t node); static gpg_error_t gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval); static int gen_card_key_with_backup (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 timestamp, u32 expireval, struct para_data_s *para); static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; if (!handle) handle = ""; buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); p = buf; if (letter || pk) { *p++ = letter; *p++ = ' '; fingerprint_from_pk (pk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); } if (*handle) { *p++ = ' '; for (i=0; handle[i] && i < 100; i++) *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; } *p = 0; write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, buf); xfree (buf); } static void print_status_key_not_created (const char *handle) { print_status_key_created (0, NULL, handle); } static void write_uid( KBNODE root, const char *s ) { PACKET *pkt = xmalloc_clear(sizeof *pkt ); size_t n = strlen(s); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 ); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy(pkt->pkt.user_id->name, s); add_kbnode( root, new_kbnode( pkt ) ); } static void do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[1]; buf[0] = 0; /* The spec says that all primary keys MUST be able to certify. */ if(sig->sig_class!=0x18) buf[0] |= 0x01; if (use & PUBKEY_USAGE_SIG) buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); } int keygen_add_key_expire (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if (pk->expiredate) { if (pk->expiredate > pk->timestamp) u = pk->expiredate - pk->timestamp; else u = 1; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } else { /* Make sure we don't leave a key expiration subpacket lying around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); } return 0; } static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; do_add_key_flags (sig, oduap->usage); return keygen_add_key_expire (sig, oduap->pk); } static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { int i; for (i=0; i < *nbuf; i++ ) if (buf[i] == val) { log_info (_("preference '%s' duplicated\n"), item); return -1; } if (*nbuf >= MAX_PREFS) { if(type==1) log_info(_("too many cipher preferences\n")); else if(type==2) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); else BUG(); return -1; } buf[(*nbuf)++] = val; return 0; } /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual * cipher/hash/compress names. Use NULL to set the default * preferences. Returns: 0 = okay */ int keygen_set_std_prefs (const char *string,int personal) { byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ char dummy_string[20*4+1]; /* Enough for 20 items. */ if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) string=opt.def_preference_list; else { int any_compress = 0; dummy_string[0]='\0'; /* The rationale why we use the order AES256,192,128 is for compatibility reasons with PGP. If gpg would define AES128 first, we would get the somewhat confusing situation: gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ /* Make sure we do not add more than 15 items here, as we could overflow the size of dummy_string. We currently have at most 12. */ if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES256) ) strcat(dummy_string,"S9 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES192) ) strcat(dummy_string,"S8 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES) ) strcat(dummy_string,"S7 "); strcat(dummy_string,"S2 "); /* 3DES */ /* The default hash algo order is: SHA-256, SHA-384, SHA-512, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224)) strcat (dummy_string, "H11 "); strcat (dummy_string, "H2 "); /* SHA-1 */ if(!check_compress_algo(COMPRESS_ALGO_ZLIB)) { strcat(dummy_string,"Z2 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) { strcat(dummy_string,"Z3 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_ZIP)) { strcat(dummy_string,"Z1 "); any_compress = 1; } /* In case we have no compress algo at all, declare that we prefer no compresssion. */ if (!any_compress) strcat(dummy_string,"Z0 "); /* Remove the trailing space. */ if (*dummy_string && dummy_string[strlen (dummy_string)-1] == ' ') dummy_string[strlen (dummy_string)-1] = 0; string=dummy_string; } } else if (!ascii_strcasecmp (string, "none")) string = ""; if(strlen(string)) { char *tok,*prefstring; prefstring=xstrdup(string); /* need a writable string! */ while((tok=strsep(&prefstring," ,"))) { if((val=string_to_cipher_algo (tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } else if((val=string_to_digest_algo (tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; } else if((val=string_to_compress_algo(tok))>-1) { if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } else if (ascii_strcasecmp(tok,"mdc")==0) mdc=1; else if (ascii_strcasecmp(tok,"no-mdc")==0) mdc=0; else if (ascii_strcasecmp(tok,"ks-modify")==0) modify=1; else if (ascii_strcasecmp(tok,"no-ks-modify")==0) modify=0; else { log_info (_("invalid item '%s' in preference string\n"),tok); rc=-1; } } xfree(prefstring); } if(!rc) { if(personal) { if(personal==PREFTYPE_SYM) { xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; else { int i; opt.personal_cipher_prefs= xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; iref=1; uid->prefs=xmalloc((sizeof(prefitem_t *)* (nsym_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;iprefs[j].type=PREFTYPE_SYM; uid->prefs[j].value=sym_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_HASH; uid->prefs[j].value=hash_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_ZIP; uid->prefs[j].value=zip_prefs[i]; } uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; uid->flags.mdc=mdc_available; uid->flags.ks_modify=ks_modify; return uid; } static void add_feature_mdc (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x01; /* MDC feature */ else buf[0] &= ~0x01; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_keyserver_modify (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; /* The keyserver modify flag is a negative flag (i.e. no-modify) */ enabled=!enabled; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x80; /* no-modify flag */ else buf[0] &= ~0x80; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); else build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); xfree (buf); } int keygen_upd_std_prefs (PKT_signature *sig, void *opaque) { (void)opaque; if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); } if (nzip_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire (sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url (sig,NULL); return 0; } int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; if(!url) url=opt.def_keyserver_url; if(url) build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); else delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } int keygen_add_notations(PKT_signature *sig,void *opaque) { struct notation *notation; /* We always start clean */ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); sig->flags.notation=0; for(notation=opaque;notation;notation=notation->next) if(!notation->flags.ignore) { unsigned char *buf; unsigned int n1,n2; n1=strlen(notation->name); if(notation->altvalue) n2=strlen(notation->altvalue); else if(notation->bdat) n2=notation->blen; else n2=strlen(notation->value); buf = xmalloc( 8 + n1 + n2 ); /* human readable or not */ buf[0] = notation->bdat?0:0x80; buf[1] = buf[2] = buf[3] = 0; buf[4] = n1 >> 8; buf[5] = n1; buf[6] = n2 >> 8; buf[7] = n2; memcpy(buf+8, notation->name, n1 ); if(notation->altvalue) memcpy(buf+8+n1, notation->altvalue, n2 ); else if(notation->bdat) memcpy(buf+8+n1, notation->bdat, n2 ); else memcpy(buf+8+n1, notation->value, n2 ); build_sig_subpkt( sig, SIGSUBPKT_NOTATION | (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), buf, 8+n1+n2 ); xfree(buf); } return 0; } int keygen_add_revkey (PKT_signature *sig, void *opaque) { struct revocation_key *revkey = opaque; byte buf[2+MAX_FINGERPRINT_LEN]; buf[0] = revkey->class; buf[1] = revkey->algid; memcpy (&buf[2], revkey->fpr, MAX_FINGERPRINT_LEN); build_sig_subpkt (sig, SIGSUBPKT_REV_KEY, buf, 2+MAX_FINGERPRINT_LEN); /* All sigs with revocation keys set are nonrevocable. */ sig->flags.revocable = 0; buf[0] = 0; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); parse_revkeys (sig); return 0; } /* Create a back-signature. If TIMESTAMP is not NULL, use it for the signature creation time. */ gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PKT_signature *backsig; cache_public_key (sub_pk); err = make_keysig_packet (&backsig, pk, NULL, sub_pk, sub_psk, 0x19, 0, 0, timestamp, 0, NULL, NULL, cache_nonce); if (err) log_error ("make_keysig_packet failed for backsig: %s\n", g10_errstr(err)); else { /* Get it into a binary packed form. */ IOBUF backsig_out = iobuf_temp(); PACKET backsig_pkt; init_packet (&backsig_pkt); backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; err = build_packet (backsig_out, &backsig_pkt); free_packet (&backsig_pkt); if (err) log_error ("build_packet failed for backsig: %s\n", g10_errstr(err)); else { size_t pktlen = 0; byte *buf = iobuf_get_temp_buffer (backsig_out); /* Remove the packet header. */ if(buf[0]&0x40) { if (buf[1] < 192) { pktlen = buf[1]; buf += 2; } else if(buf[1] < 224) { pktlen = (buf[1]-192)*256; pktlen += buf[2]+192; buf += 3; } else if (buf[1] == 255) { pktlen = buf[2] << 24; pktlen |= buf[3] << 16; pktlen |= buf[4] << 8; pktlen |= buf[5]; buf += 6; } else BUG (); } else { int mark = 1; switch (buf[0]&3) { case 3: BUG (); break; case 2: pktlen = buf[mark++] << 24; pktlen |= buf[mark++] << 16; case 1: pktlen |= buf[mark++] << 8; case 0: pktlen |= buf[mark++]; } buf += mark; } /* Now make the binary blob into a subpacket. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); iobuf_close (backsig_out); } } return err; } /* Write a direct key signature to the first key in ROOT using the key PSK. REVKEY is describes the direct key signature and TIMESTAMP is the timestamp to set on the signature. */ static gpg_error_t write_direct_sig (KBNODE root, PKT_public_key *psk, struct revocation_key *revkey, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing direct signature\n")); /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (&sig, pk, NULL,NULL, psk, 0x1F, 0, 0, timestamp, 0, keygen_add_revkey, revkey, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", g10_errstr (err) ); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write a self-signature to the first user id in ROOT using the key PSK. USE and TIMESTAMP give the extra data we need for the signature. */ static gpg_error_t write_selfsigs (KBNODE root, PKT_public_key *psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing self signature\n")); /* Get the uid packet from the list. */ node = find_kbnode (root, PKT_USER_ID); if (!node) BUG(); /* No user id packet in tree. */ uid = node->pkt->pkt.user_id; /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pk = node->pkt->pkt.public_key; /* The usage has not yet been set - do it now. */ pk->pubkey_usage = use; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (&sig, pk, uid, NULL, psk, 0x13, 0, 0, timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", g10_errstr (err)); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write the key binding signature. If TIMESTAMP is not NULL use the signature creation time. PRI_PSK is the key use for signing. SUB_PSK is a key used to create a back-signature; that one is only used if USE has the PUBKEY_USAGE_SIG capability. */ static int write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; 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")); /* Get the primary pk packet from the tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pri_pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the * signature creation is able to retrieve the public key. */ cache_public_key (pri_pk); /* Find the last subkey. */ sub_pk = NULL; for (node = root; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; } if (!sub_pk) BUG(); /* Make the signature. */ oduap.usage = use; oduap.pk = sub_pk; err = make_keysig_packet (&sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, 0, 0, timestamp, 0, keygen_add_key_flags_and_expire, &oduap, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", g10_errstr (err)); return err; } /* Make a backsig. */ if (use & PUBKEY_USAGE_SIG) { err = make_backsig (sig, pri_pk, sub_pk, sub_psk, timestamp, cache_nonce); if (err) return err; } pkt = xmalloc_clear ( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt) ); return err; } static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) { gpg_error_t err; gcry_sexp_t list, l2; char *curve; int i; const char *oidstr; unsigned int nbits; array[0] = NULL; array[1] = NULL; array[2] = NULL; list = gcry_sexp_find_token (sexp, "public-key", 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); l2 = gcry_sexp_find_token (list, "curve", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } curve = gcry_sexp_nth_string (l2, 1); if (!curve) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } gcry_sexp_release (l2); oidstr = openpgp_curve_to_oid (curve, &nbits); if (!oidstr) { /* That can't happen because we used one of the curves gpg_curve_to_oid knows about. */ err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; l2 = gcry_sexp_find_token (list, "q", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[1]) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } gcry_sexp_release (list); if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) { err = gpg_error_from_syserror (); goto leave; } } leave: if (err) { for (i=0; i < 3; i++) { gcry_mpi_release (array[i]); array[i] = NULL; } } return err; } /* Extract key parameters from SEXP and store them in ARRAY. ELEMS is a string where each character denotes a parameter name. TOPNAME is the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) { gcry_sexp_t list, l2; const char *s; int i, idx; int rc = 0; list = gcry_sexp_find_token (sexp, topname, 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); for (idx=0,s=elems; *s; s++, idx++) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { rc = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; } } gcry_sexp_release (list); leave: if (rc) { for (i=0; itimestamp = timestamp; pk->version = 4; if (expireval) 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); 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); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* Common code for the key generation fucntion gen_xxx. */ static int common_gen (const char *keyparms, int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { int err; PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; err = agent_genkey (NULL, cache_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), &s_key); if (err) { log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); return err; } pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); return err; } pk->timestamp = timestamp; pk->version = 4; if (expireval) 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); 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); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* * Generate an Elgamal key. */ static int gen_elg (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; assert (is_ELGAMAL (algo)); if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } /* Note that we use transient-key only if no-protection has also been enabled. */ snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)%s))", algo == GCRY_PK_ELG_E ? "openpgp-elg" : algo == GCRY_PK_ELG ? "elg" : "x-oops" , strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an DSA key */ static gpg_error_t gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { int err; unsigned int qbits; char *keyparms; char nbitsstr[35]; char qbitsstr[35]; if (nbits < 768) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if ( nbits > 3072 ) { nbits = 3072; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* To comply with FIPS rules we round up to the next value unless in expert mode. */ if (!opt.expert && nbits > 1024 && (nbits % 1024)) { nbits = ((nbits + 1023) / 1024) * 1024; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* Figure out a q size based on the key size. FIPS 180-3 says: L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ if (nbits > 2047) qbits = 256; else if ( nbits > 1024) qbits = 224; else qbits = 160; if (qbits != 160 ) log_info (_("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n")); snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); keyparms = xtryasprintf ("(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, strlen (qbitsstr), qbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an ECC key */ static gpg_error_t gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { gpg_error_t err; char *keyparms; assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ if (algo == PUBKEY_ALGO_EDDSA) keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); else keyparms = 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 (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); } return err; } /* * Generate an RSA key. */ static int gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, char **cache_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; assert (is_RSA(algo)); if (!nbits) nbits = DEFAULT_STD_KEYSIZE; if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, cache_nonce_addr); xfree (keyparms); } return err; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !digitp(s) ) return 0; for( s++; *s; s++) if( !digitp(s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } static void print_key_flags(int flags) { if(flags&PUBKEY_USAGE_SIG) tty_printf("%s ",_("Sign")); if(flags&PUBKEY_USAGE_CERT) tty_printf("%s ",_("Certify")); if(flags&PUBKEY_USAGE_ENC) tty_printf("%s ",_("Encrypt")); if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); } /* Returns the key flags */ static unsigned int ask_key_flags(int algo,int subkey) { /* TRANSLATORS: Please use only plain ASCII characters for the translation. If this is not possible use single digits. The string needs to 8 bytes long. Here is a description of the functions: s = Toggle signing capability e = Toggle encryption capability a = Toggle authentication capability q = Finish */ const char *togglers=_("SsEeAaQq"); char *answer=NULL; unsigned int current=0; unsigned int possible=openpgp_pk_algo_usage(algo); if ( strlen(togglers) != 8 ) { tty_printf ("NOTE: Bad translation at %s:%d. " "Please report.\n", __FILE__, __LINE__); togglers = "11223300"; } /* Only primary keys may certify. */ if(subkey) possible&=~PUBKEY_USAGE_CERT; /* Preload the current set with the possible set, minus authentication, since nobody really uses auth yet. */ current=possible&~PUBKEY_USAGE_AUTH; for(;;) { tty_printf("\n"); tty_printf(_("Possible actions for a %s key: "), openpgp_pk_algo_name (algo)); print_key_flags(possible); tty_printf("\n"); tty_printf(_("Current allowed actions: ")); print_key_flags(current); tty_printf("\n\n"); if(possible&PUBKEY_USAGE_SIG) tty_printf(_(" (%c) Toggle the sign capability\n"), togglers[0]); if(possible&PUBKEY_USAGE_ENC) tty_printf(_(" (%c) Toggle the encrypt capability\n"), togglers[2]); if(possible&PUBKEY_USAGE_AUTH) tty_printf(_(" (%c) Toggle the authenticate capability\n"), togglers[4]); tty_printf(_(" (%c) Finished\n"),togglers[6]); tty_printf("\n"); xfree(answer); answer = cpr_get("keygen.flags",_("Your selection? ")); cpr_kill_prompt(); if(strlen(answer)>1) tty_printf(_("Invalid selection.\n")); else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) break; else if((*answer==togglers[0] || *answer==togglers[1]) && possible&PUBKEY_USAGE_SIG) { if(current&PUBKEY_USAGE_SIG) current&=~PUBKEY_USAGE_SIG; else current|=PUBKEY_USAGE_SIG; } else if((*answer==togglers[2] || *answer==togglers[3]) && possible&PUBKEY_USAGE_ENC) { if(current&PUBKEY_USAGE_ENC) current&=~PUBKEY_USAGE_ENC; else current|=PUBKEY_USAGE_ENC; } else if((*answer==togglers[4] || *answer==togglers[5]) && possible&PUBKEY_USAGE_AUTH) { if(current&PUBKEY_USAGE_AUTH) current&=~PUBKEY_USAGE_AUTH; else current|=PUBKEY_USAGE_AUTH; } else tty_printf(_("Invalid selection.\n")); } xfree(answer); return current; } /* Check whether we have a key for the key with HEXGRIP. Returns 0 if there is no such key or the OpenPGP algo number for the key. */ static int check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; unsigned char *public; size_t publiclen; const char *algostr; if (hexgrip[0] == '&') hexgrip++; err = agent_readkey (ctrl, 0, hexgrip, &public); if (err) return 0; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); get_pk_algo_from_canon_sexp (public, publiclen, &algostr); xfree (public); /* FIXME: Mapping of ECC algorithms is probably not correct. */ if (!algostr) return 0; else if (!strcmp (algostr, "rsa")) return PUBKEY_ALGO_RSA; else if (!strcmp (algostr, "dsa")) return PUBKEY_ALGO_DSA; else if (!strcmp (algostr, "elg")) return PUBKEY_ALGO_ELGAMAL_E; else if (!strcmp (algostr, "ecc")) return PUBKEY_ALGO_ECDH; else if (!strcmp (algostr, "ecdsa")) return PUBKEY_ALGO_ECDSA; else if (!strcmp (algostr, "eddsa")) return PUBKEY_ALGO_EDDSA; else return 0; } /* Ask for an algorithm. The function returns the algorithm id to * create. If ADDMODE is false the function won't show an option to * create the primary and subkey combined and won't set R_USAGE * either. If a combined algorithm has been selected, the subkey * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the * user has the choice to enter the keygrip of an existing key. That * keygrip is then stored at this address. The caller needs to free * it. */ static int ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, char **r_keygrip) { char *keygrip = NULL; char *answer; int algo; int dummy_algo; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; tty_printf (_("Please select what kind of key you want:\n")); #if GPG_USE_RSA if (!addmode) tty_printf (_(" (%d) RSA and RSA (default)\n"), 1 ); #endif if (!addmode) tty_printf (_(" (%d) DSA and Elgamal\n"), 2 ); tty_printf (_(" (%d) DSA (sign only)\n"), 3 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (sign only)\n"), 4 ); #endif if (addmode) { tty_printf (_(" (%d) Elgamal (encrypt only)\n"), 5 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (encrypt only)\n"), 6 ); #endif } if (opt.expert) { tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); #endif } #if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA if (opt.expert && !addmode) tty_printf (_(" (%d) ECC\n"), 9 ); if (opt.expert) tty_printf (_(" (%d) ECC (sign only)\n"), 10 ); if (opt.expert) tty_printf (_(" (%d) ECC (set your own capabilities)\n"), 11 ); if (opt.expert && addmode) tty_printf (_(" (%d) ECC (encrypt only)\n"), 12 ); #endif if (opt.expert && r_keygrip) tty_printf (_(" (%d) Existing key\n"), 13 ); for (;;) { *r_usage = 0; *r_subkey_algo = 0; answer = cpr_get ("keygen.algo", _("Your selection? ")); cpr_kill_prompt (); algo = *answer? atoi (answer) : 1; xfree(answer); answer = NULL; if (algo == 1 && !addmode) { algo = PUBKEY_ALGO_RSA; *r_subkey_algo = PUBKEY_ALGO_RSA; break; } else if (algo == 2 && !addmode) { algo = PUBKEY_ALGO_DSA; *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if (algo == 3) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 4) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 5 && addmode) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } else if (algo == 6 && addmode) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_ENC; break; } else if (algo == 7 && opt.expert) { algo = PUBKEY_ALGO_DSA; *r_usage = ask_key_flags (algo, addmode); break; } else if (algo == 8 && opt.expert) { algo = PUBKEY_ALGO_RSA; *r_usage = ask_key_flags (algo, addmode); break; } else if (algo == 9 && opt.expert && !addmode) { algo = PUBKEY_ALGO_ECDSA; *r_subkey_algo = PUBKEY_ALGO_ECDH; break; } else if (algo == 10 && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 11 && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = ask_key_flags (algo, addmode); break; } else if (algo == 12 && opt.expert && addmode) { algo = PUBKEY_ALGO_ECDH; *r_usage = PUBKEY_USAGE_ENC; break; } else if (algo == 13 && opt.expert && r_keygrip) { for (;;) { xfree (answer); answer = tty_get (_("Enter the keygrip: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) { xfree (answer); answer = NULL; continue; } if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(algo = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; *r_usage = ask_key_flags (algo, addmode); break; } else tty_printf (_("Invalid selection.\n")); } if (r_keygrip) *r_keygrip = keygrip; return algo; } /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { unsigned int nbits, min, def = DEFAULT_STD_KEYSIZE, max=4096; int for_subkey = !!primary_keysize; int autocomp = 0; if(opt.expert) min=512; else min=1024; if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) nbits = 3072; /* For performance reasons we don't support more than 3072 bit DSA. However we won't see this case anyway because DSA can't be used as an encryption subkey ;-). */ else nbits = primary_keysize; autocomp = 1; goto leave; } switch(algo) { case PUBKEY_ALGO_DSA: def=2048; max=3072; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: min=256; def=256; max=521; break; case PUBKEY_ALGO_EDDSA: min=255; def=255; max=441; break; case PUBKEY_ALGO_RSA: min=1024; break; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); for (;;) { char *prompt, *answer; if (for_subkey) prompt = xasprintf (_("What keysize do you want " "for the subkey? (%u) "), def); else prompt = xasprintf (_("What keysize do you want? (%u) "), def); answer = cpr_get ("keygen.size", prompt); cpr_kill_prompt (); nbits = *answer? atoi (answer): def; xfree(prompt); xfree(answer); if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), openpgp_pk_algo_name (algo), min, max); else break; } tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; if (!autocomp) 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 (!autocomp) tty_printf (_("rounded to %u bits\n"), nbits); } } 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 (!autocomp) tty_printf (_("rounded to %u bits\n"), nbits); } } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; if (!autocomp) tty_printf (_("rounded up to %u bits\n"), nbits ); } return nbits; } /* Ask for the curve. ALGO is the selected algorithm which this function may adjust. Returns a malloced string with the name of the curve. BOTH tells that gpg creates a primary and subkey. */ static char * ask_curve (int *algo, int both) { struct { const char *name; int available; int expert_only; int fix_curve; const char *pretty_name; } curves[] = { #if GPG_USE_EDDSA { "Curve25519", 0, 0, 1, "Curve 25519" }, #endif #if GPG_USE_ECDSA || GPG_USE_ECDH { "NIST P-256", 0, 1, 0, }, { "NIST P-384", 0, 0, 0, }, { "NIST P-521", 0, 1, 0, }, { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" }, { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" }, { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" }, { "secp256k1", 0, 1, 0 }, #endif }; int idx; char *answer; char *result = NULL; gcry_sexp_t keyparms; tty_printf (_("Please select which elliptic curve you want:\n")); again: keyparms = NULL; for (idx=0; idx < DIM(curves); idx++) { int rc; curves[idx].available = 0; if (!opt.expert && curves[idx].expert_only) continue; /* FIXME: The strcmp below is a temporary hack during development. It shall be removed as soon as we have proper Curve25519 support in Libgcrypt. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", (!strcmp (curves[idx].name, "Curve25519") ? "Ed25519" : curves[idx].name)); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; if (both && curves[idx].fix_curve) { /* Both Curve 25519 keys are to be created. Check that Libgcrypt also supports the real Curve25519. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; } curves[idx].available = 1; tty_printf (" (%d) %s\n", idx + 1, curves[idx].pretty_name? curves[idx].pretty_name:curves[idx].name); } gcry_sexp_release (keyparms); for (;;) { answer = cpr_get ("keygen.curve", _("Your selection? ")); cpr_kill_prompt (); idx = *answer? atoi (answer) : 1; if (*answer && !idx) { /* See whether the user entered the name of the curve. */ for (idx=0; idx < DIM(curves); idx++) { if (!opt.expert && curves[idx].expert_only) continue; if (!stricmp (curves[idx].name, answer) || (curves[idx].pretty_name && !stricmp (curves[idx].pretty_name, answer))) break; } if (idx == DIM(curves)) idx = -1; } else idx--; xfree(answer); answer = NULL; if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) tty_printf (_("Invalid selection.\n")); else { if (curves[idx].fix_curve) { log_info ("WARNING: Curve25519 is not yet part of the" " OpenPGP standard.\n"); if (!cpr_get_answer_is_yes("experimental_curve.override", "Use this curve anyway? (y/N) ") ) goto again; } /* If the user selected a signing algorithm and Curve25519 we need to update the algo and and the curve name. */ if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) && curves[idx].fix_curve) { *algo = PUBKEY_ALGO_EDDSA; result = xstrdup ("Ed25519"); } else result = xstrdup (curves[idx].name); break; } } if (!result) result = xstrdup (curves[0].name); return result; } /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. * This isn't perfect since scan_isodatestr returns unix time, and * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. * Because of this, we only permit setting expirations up to 2106, but * OpenPGP could theoretically allow up to 2242. I think we'll all * just cope for the next few years until we get a 64-bit time_t or * similar. */ u32 parse_expire_string( const char *string ) { int mult; u32 seconds; u32 abs_date = 0; u32 curtime = make_timestamp (); time_t tt; if (!*string) seconds = 0; else if (!strncmp (string, "seconds=", 8)) seconds = atoi (string+8); else if ((abs_date = scan_isodatestr(string)) && (abs_date+86400/2) > curtime) seconds = (abs_date+86400/2) - curtime; else if ((tt = isotime2epoch (string)) != (time_t)(-1)) seconds = (u32)tt - curtime; else if ((mult = check_valid_days (string))) seconds = atoi (string) * 86400L * mult; else seconds = (u32)(-1); return seconds; } /* Parsean Creation-Date string which is either "1986-04-26" or "19860426T042640". Returns 0 on error. */ static u32 parse_creation_string (const char *string) { u32 seconds; if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = atoi (string+8); else if ( !(seconds = scan_isodatestr (string))) { time_t tmp = isotime2epoch (string); seconds = (tmp == (time_t)(-1))? 0 : tmp; } return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 ask_expire_interval(int object,const char *def_expire) { u32 interval; char *answer; switch(object) { case 0: if(def_expire) BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); break; case 1: if(!def_expire) BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " = signature expires in n days\n" " w = signature expires in n weeks\n" " m = signature expires in n months\n" " y = signature expires in n years\n")); break; default: BUG(); } /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { u32 curtime; xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else { char *prompt; #define PROMPTSTRING _("Signature is valid for? (%s) ") /* This will actually end up larger than necessary because of the 2 bytes for '%s' */ prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1); sprintf(prompt,PROMPTSTRING,def_expire); #undef PROMPTSTRING answer = cpr_get("siggen.valid",prompt); xfree(prompt); if(*answer=='\0') answer=xstrdup(def_expire); } cpr_kill_prompt(); trim_spaces(answer); curtime = make_timestamp (); interval = parse_expire_string( answer ); if( interval == (u32)-1 ) { tty_printf(_("invalid value\n")); continue; } if( !interval ) { tty_printf((object==0) ? _("Key does not expire at all\n") : _("Signature does not expire at all\n")); } else { tty_printf(object==0 ? _("Key expires at %s\n") : _("Signature expires at %s\n"), asctimestamp((ulong)(curtime + interval) ) ); #if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T) if ( (time_t)((ulong)(curtime+interval)) < 0 ) tty_printf (_("Your system can't display dates beyond 2038.\n" "However, it will be correctly handled up to" " 2106.\n")); else #endif /*SIZEOF_TIME_T*/ if ( (time_t)((unsigned long)(curtime+interval)) < curtime ) { tty_printf (_("invalid value\n")); continue; } } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct? (y/N) ")) ) break; } xfree(answer); return interval; } u32 ask_expiredate() { u32 x = ask_expire_interval(0,NULL); return x? make_timestamp() + x : 0; } static PKT_user_id * uid_from_string (const char *string) { size_t n; PKT_user_id *uid; n = strlen (string); uid = xmalloc_clear (sizeof *uid + n); uid->len = n; strcpy (uid->name, string); uid->ref = 1; return uid; } /* Ask for a user ID. With a MODE of 1 an extra help prompt is printed for use during a new key creation. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. */ static char * ask_user_id (int mode, KBNODE keyblock) { char *answer; char *aname, *acomment, *amail, *uid; if ( !mode ) { /* TRANSLATORS: This is the new string telling the user what gpg is now going to do (i.e. ask for the parts of the user ID). Note that if you do not translate this string, a different string will be used used, which might still have a correct translation. */ const char *s1 = N_("\n" "GnuPG needs to construct a user ID to identify your key.\n" "\n"); const char *s2 = _(s1); if (!strcmp (s1, s2)) { /* There is no translation for the string thus we to use the old info text. gettext has no way to tell whether a translation is actually available, thus we need to to compare again. */ /* TRANSLATORS: This string is in general not anymore used but you should keep your existing translation. In case the new string is not translated this old string will be used. */ const char *s3 = N_("\n" "You need a user ID to identify your key; " "the software constructs the user ID\n" "from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n"); const char *s4 = _(s3); if (strcmp (s3, s4)) s2 = s3; /* A translation exists - use it. */ } tty_printf ("%s", s2) ; } uid = aname = acomment = amail = NULL; for(;;) { char *p; int fail=0; if( !aname ) { for(;;) { xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( opt.allow_freeform_uid ) break; if( strpbrk( aname, "<>" ) ) tty_printf(_("Invalid character in name\n")); else if( digitp(aname) ) tty_printf(_("Name may not start with a digit\n")); else if( strlen(aname) < 5 ) tty_printf(_("Name must be at least 5 characters long\n")); else break; } } if( !amail ) { for(;;) { xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ else if ( !is_valid_mailbox (amail) ) tty_printf(_("Not a valid email address\n")); else break; } } if( !acomment ) { for(;;) { xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } xfree(uid); uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); p = stpcpy(p, aname ); if( *acomment ) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if( *amail ) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); /* Append a warning if the RNG is switched into fake mode. */ if ( random_is_faked () ) strcpy(p, " (insecure!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the '%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); if( !*amail && !opt.allow_freeform_uid && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } if (!fail && keyblock) { PKT_user_id *uidpkt = uid_from_string (uid); KBNODE node; for (node=keyblock; node && !fail; node=node->next) if (!is_deleted_kbnode (node) && node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id)) fail = 1; if (fail) tty_printf (_("Such a user ID already exists on this key!\n")); free_user_id (uidpkt); } for(;;) { /* TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. n = Change name c = Change comment e = Change email o = Okay (ready, continue) q = Quit */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = xstrdup (ansstr + (fail?8:6)); answer[1] = 0; } else { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { if( fail ) { tty_printf(_("Please correct the error first\n")); } else { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; xfree(uid); uid = NULL; break; } xfree(answer); } xfree(answer); if( !amail && !acomment && !amail ) break; xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); xfree( uid ); uid = p; } return uid; } /* MODE 0 - standard 1 - Ask for passphrase of the card backup key. */ static DEK * do_ask_passphrase (STRING2KEY **ret_s2k, int mode, int *r_canceled) { DEK *dek = NULL; STRING2KEY *s2k; const char *errtext = NULL; const char *custdesc = NULL; tty_printf(_("You need a Passphrase to protect your secret key.\n\n") ); if (mode == 1) custdesc = _("Please enter a passphrase to protect the off-card " "backup of the new encryption key."); s2k = xmalloc_secure( sizeof *s2k ); for(;;) { s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; dek = passphrase_to_dek_ext (NULL, 0, opt.s2k_cipher_algo, s2k, 2, errtext, custdesc, NULL, r_canceled); if (!dek && *r_canceled) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; break; } else if( !dek ) { errtext = N_("passphrase not correctly repeated; try again"); tty_printf(_("%s.\n"), _(errtext)); } else if( !dek->keylen ) { xfree(dek); dek = NULL; xfree(s2k); s2k = NULL; tty_printf(_( "You don't want a passphrase - this is probably a *bad* idea!\n" "I will do it anyway. You can change your passphrase at any time,\n" "using this program with the option \"--edit-key\".\n\n")); break; } else break; /* okay */ } *ret_s2k = s2k; return dek; } /* Basic key generation. Here we divert to the actual generation routines based on the requested algorithm. */ static int do_create (int algo, unsigned int nbits, const char *curve, KBNODE pub_root, u32 timestamp, u32 expiredate, int is_subkey, int keygen_flags, char **cache_nonce_addr) { gpg_error_t err; /* Fixme: The entropy collecting message should be moved to a libgcrypt progress handler. */ if (!opt.batch) tty_printf (_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if (algo == PUBKEY_ALGO_ELGAMAL_E) err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, cache_nonce_addr); else BUG(); return err; } /* Generate a new user id packet or return NULL if canceled. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. */ PKT_user_id * generate_user_id (KBNODE keyblock) { char *p; p = ask_user_id (1, keyblock); if (!p) return NULL; /* Canceled. */ return uid_from_string (p); } /* Append R to the linked list PARA. */ static void append_to_parameter (struct para_data_s *para, struct para_data_s *r) { assert (para); while (para->next) para = para->next; para->next = r; } /* Release the parameter list R. */ static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; if (r->key == pPASSPHRASE_DEK) xfree (r->u.dek); else if (r->key == pPASSPHRASE_S2K ) xfree (r->u.s2k); xfree (r); } } static struct para_data_s * get_parameter( struct para_data_s *para, enum para_name key ) { struct para_data_s *r; for( r = para; r && r->key != key; r = r->next ) ; return r; } static const char * get_parameter_value( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return (r && *r->u.value)? r->u.value : NULL; } static int get_parameter_algo( struct para_data_s *para, enum para_name key, int *r_default) { int i; struct para_data_s *r = get_parameter( para, key ); if (r_default) *r_default = 0; if (!r) return -1; if (!ascii_strcasecmp (r->u.value, "default")) { /* Note: If you change this default algo, remember to change it also in gpg.c:gpgconf_list. */ i = DEFAULT_STD_ALGO; if (r_default) *r_default = 1; } else if (digitp (r->u.value)) i = atoi( r->u.value ); else if (!strcmp (r->u.value, "ELG-E") || !strcmp (r->u.value, "ELG")) i = GCRY_PK_ELG_E; else i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value)); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; } /* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. */ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); char *p, *pn; unsigned int use; if( !r ) return 0; /* none (this is an optional parameter)*/ use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { if ( !*p) ; else if ( !ascii_strcasecmp (p, "sign") ) use |= PUBKEY_USAGE_SIG; else if ( !ascii_strcasecmp (p, "encrypt") ) use |= PUBKEY_USAGE_ENC; else if ( !ascii_strcasecmp (p, "auth") ) use |= PUBKEY_USAGE_AUTH; else { log_error("%s:%d: invalid usage list\n", fname, r->lnr ); return -1; /* error */ } } r->u.usage = use; return 1; } static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); struct revocation_key revkey; char *pn; int i; if( !r ) return 0; /* none (this is an optional parameter) */ pn = r->u.value; revkey.class=0x80; revkey.algid=atoi(pn); if(!revkey.algid) goto fail; /* Skip to the fpr */ while(*pn && *pn!=':') pn++; if(*pn!=':') goto fail; pn++; for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); return 0; fail: log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); return -1; /* error */ } static u32 get_parameter_u32( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); if( !r ) return 0; if( r->key == pKEYCREATIONDATE ) return r->u.creation; if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; return (unsigned int)strtoul( r->u.value, NULL, 10 ); } static unsigned int get_parameter_uint( struct para_data_s *para, enum para_name key ) { return get_parameter_u32( para, key ); } static struct revocation_key * get_parameter_revkey( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? &r->u.revkey : NULL; } static int proc_parameter_file( struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; size_t n; char *p; int is_default = 0; int have_user_id = 0; int err, algo; /* Check that we have all required parameters. */ r = get_parameter( para, pKEYTYPE ); if(r) { algo = get_parameter_algo (para, pKEYTYPE, &is_default); if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } } else { log_error ("%s: no Key-Type specified\n",fname); return -1; } err = parse_parameter_usage (fname, para, pKEYUSAGE); if (!err) { /* Default to algo capabilities if key-usage is not provided and no default algorithm has been requested. */ r = xmalloc_clear(sizeof(*r)); r->key = pKEYUSAGE; r->u.usage = (is_default ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG) : openpgp_pk_algo_usage(algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", fname, r->lnr, algo); return -1; } } is_default = 0; r = get_parameter( para, pSUBKEYTYPE ); if(r) { algo = get_parameter_algo (para, pSUBKEYTYPE, &is_default); if (openpgp_pk_test_algo (algo)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); if (!err) { /* Default to algo capabilities if subkey-usage is not provided */ r = xmalloc_clear (sizeof(*r)); r->key = pSUBKEYUSAGE; r->u.usage = (is_default ? PUBKEY_USAGE_ENC : openpgp_pk_algo_usage (algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pSUBKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Subkey-Usage not allowed" " for algo %d\n", fname, r->lnr, algo); return -1; } } } if( get_parameter_value( para, pUSERID ) ) have_user_id=1; else { /* create the formatted user ID */ s1 = get_parameter_value( para, pNAMEREAL ); s2 = get_parameter_value( para, pNAMECOMMENT ); s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); r = xmalloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) p = stpcpy(p, s1 ); if( s2 ) p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); if( s3 ) p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); append_to_parameter (para, r); have_user_id=1; } } if(!have_user_id) { log_error("%s: no User-ID specified\n",fname); return -1; } /* Set preferences, if any. */ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); /* Set keyserver, if any. */ s1=get_parameter_value( para, pKEYSERVER ); if(s1) { struct keyserver_spec *spec; spec=parse_keyserver_uri(s1,1,NULL,0); if(spec) { free_keyserver_spec(spec); opt.def_keyserver_url=s1; } else { log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); return -1; } } /* Set revoker, if any. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; /* Make DEK and S2K from the Passphrase. */ if (outctrl->ask_passphrase) { /* %ask-passphrase is active - ignore pPASSPRASE and ask. This feature is required so that GUIs are able to do a key creation but have gpg-agent ask for the passphrase. */ int canceled = 0; STRING2KEY *s2k; DEK *dek; dek = do_ask_passphrase (&s2k, 0, &canceled); if (dek) { r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_DEK; r->u.dek = dek; append_to_parameter (para, r); r = xmalloc_clear( sizeof *r ); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; append_to_parameter (para, r); } if (canceled) { log_error ("%s:%d: key generation canceled\n", fname, r->lnr ); return -1; } } else { r = get_parameter( para, pPASSPHRASE ); if ( r && *r->u.value ) { /* We have a plain text passphrase - create a DEK from it. * It is a little bit ridiculous to keep it in secure memory * but because we do this always, why not here. */ STRING2KEY *s2k; DEK *dek; s2k = xmalloc ( sizeof *s2k ); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; set_next_passphrase ( r->u.value ); dek = passphrase_to_dek (NULL, 0, opt.s2k_cipher_algo, s2k, 2, NULL, NULL); if (!dek) { log_error ("%s:%d: error post processing the passphrase\n", fname, r->lnr ); xfree (s2k); return -1; } set_next_passphrase (NULL); memset (r->u.value, 0, strlen(r->u.value)); r = xmalloc_clear (sizeof *r); r->key = pPASSPHRASE_S2K; r->u.s2k = s2k; append_to_parameter (para, r); r = xmalloc_clear (sizeof *r); r->key = pPASSPHRASE_DEK; r->u.dek = dek; append_to_parameter (para, r); } } /* Make KEYCREATIONDATE from Creation-Date. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { u32 seconds; seconds = parse_creation_string (r->u.value); if (!seconds) { log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); return -1; } r->u.creation = seconds; r->key = pKEYCREATIONDATE; /* Change that entry. */ } /* Make KEYEXPIRE from Expire-Date. */ r = get_parameter( para, pEXPIREDATE ); if( r && *r->u.value ) { u32 seconds; seconds = parse_expire_string( r->u.value ); if( seconds == (u32)-1 ) { log_error("%s:%d: invalid expire date\n", fname, r->lnr ); return -1; } r->u.expire = seconds; r->key = pKEYEXPIRE; /* change hat entry */ /* also set it for the subkey */ r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = seconds; append_to_parameter (para, r); } do_generate_keypair( para, outctrl, card ); return 0; } /**************** * Kludge to allow non interactive key generation controlled * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void read_parameter_file( const char *fname ) { static struct { const char *name; enum para_name key; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, { "Subkey-Curve", pSUBKEYCURVE }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, { "Name-Comment", pNAMECOMMENT }, { "Expire-Date", pEXPIREDATE }, { "Creation-Date", pCREATIONDATE }, { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, { "Handle", pHANDLE }, { "Keyserver", pKEYSERVER }, { NULL, 0 } }; IOBUF fp; byte *line; unsigned int maxlen, nline; char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; int i; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); outctrl.pub.afx = new_armor_context (); if( !fname || !*fname) fname = "-"; fp = iobuf_open (fname); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; gpg_err_set_errno (EPERM); } if (!fp) { log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); return; } iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL); lnr = 0; err = NULL; para = NULL; maxlen = 1024; line = NULL; while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; if( !maxlen ) { err = "line too long"; break; } for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%ask-passphrase" ) ) outctrl.ask_passphrase = 1; else if( !ascii_strcasecmp( keyword, "%no-ask-passphrase" ) ) outctrl.ask_passphrase = 0; else if( !ascii_strcasecmp( keyword, "%no-protection" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_NO_PROTECTION; else if( !ascii_strcasecmp( keyword, "%transient-key" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_TRANSIENT_KEY; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.pub.newfname ); outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } else if( !ascii_strcasecmp( keyword, "%secring" ) ) { /* Ignore this command. */ } else log_info("skipping control '%s' (%s)\n", keyword, value ); continue; } if( !(p = strchr( p, ':' )) || p == keyword ) { err = "missing colon"; break; } if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; break; } value = p; trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { err = "unknown keyword"; break; } if( keywords[i].key != pKEYTYPE && !para ) { err = "parameter block does not start with \"Key-Type\""; break; } if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else { for( r = para; r; r = r->next ) { if( r->key == keywords[i].key ) break; } if( r ) { err = "duplicate keyword"; break; } } r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); r->next = para; para = r; } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); else if( iobuf_error (fp) ) { log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; if (proc_parameter_file( para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl.pub.fname); xfree( outctrl.pub.fname ); xfree( outctrl.pub.newfname ); } release_parameter_list( para ); iobuf_close (fp); release_armor_context (outctrl.pub.afx); } /* Helper for quick_generate_keypair. */ static struct para_data_s * quickgen_set_para (struct para_data_s *para, int for_subkey, int algo, int nbits, const char *curve) { struct para_data_s *r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; strcpy (r->u.value, for_subkey ? "encrypt" : "sign"); r->next = para; para = r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYTYPE : pKEYTYPE; sprintf (r->u.value, "%d", algo); r->next = para; para = r; if (curve) { r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = for_subkey? pSUBKEYCURVE : pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYLENGTH : pKEYLENGTH; sprintf (r->u.value, "%u", nbits); r->next = para; para = r; } return para; } /* * Unattended generation of a standard key. */ void quick_generate_keypair (const char *uid) { gpg_error_t err; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; int use_tty; memset (&outctrl, 0, sizeof outctrl); use_tty = (!opt.batch && !opt.answer_yes && !cpr_enabled () && gnupg_isatty (fileno (stdin)) && gnupg_isatty (fileno (stdout)) && gnupg_isatty (fileno (stderr))); r = xmalloc_clear (sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; uid = trim_spaces (r->u.value); if (!*uid || (!opt.allow_freeform_uid && !is_valid_user_id (uid))) { log_error (_("Key generation failed: %s\n"), gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } /* If gpg is directly used on the console ask whether a key with the given user id shall really be created. */ if (use_tty) { tty_printf (_("About to create a key for:\n \"%s\"\n\n"), uid); if (!cpr_get_answer_is_yes_def ("quick_keygen.okay", _("Continue? (Y/n) "), 1)) goto leave; } /* Check whether such a user ID already exists. */ { KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = uid; kdbhd = keydb_new (); err = keydb_search (kdbhd, &desc, 1, NULL); keydb_release (kdbhd); if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_info (_("A key for \"%s\" already exists\n"), uid); if (opt.answer_yes) ; else if (!use_tty || !cpr_get_answer_is_yes_def ("quick_keygen.force", _("Create anyway? (y/N) "), 0)) { log_inc_errorcount (); /* we used log_info */ goto leave; } log_info (_("creating anyway\n")); } } para = quickgen_set_para (para, 0, PUBKEY_ALGO_RSA, 2048, NULL); para = quickgen_set_para (para, 1, PUBKEY_ALGO_RSA, 2048, NULL); /* para = quickgen_set_para (para, 0, PUBKEY_ALGO_EDDSA, 0, "Ed25519"); */ /* para = quickgen_set_para (para, 1, PUBKEY_ALGO_ECDH, 0, "Curve25519"); */ proc_parameter_file (para, "[internal]", &outctrl, 0); leave: release_parameter_list (para); } /* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the function will create the keys on an * OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is * NOT NULL, the encryption key for the card is generated on the host, * imported to the card and a backup file created by gpg-agent. */ void generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, int card_backup_key) { unsigned int nbits; char *uid = NULL; int algo; unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; #ifndef ENABLE_CARD_SUPPORT (void)card_backup_key; #endif memset( &outctrl, 0, sizeof( outctrl ) ); if (opt.batch && card_serialno) { /* We don't yet support unattended key generation. */ log_error (_("can't do this in batch mode\n")); return; } if (opt.batch) { read_parameter_file( fname ); return; } if (card_serialno) { #ifdef ENABLE_CARD_SUPPORT r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; algo = PUBKEY_ALGO_RSA; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy (r->u.value, "sign"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy (r->u.value, "encrypt"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (card_backup_key) { r = xcalloc (1, sizeof *r + 1); r->key = pCARDBACKUPKEY; strcpy (r->u.value, "1"); r->next = para; para = r; } #endif /*ENABLE_CARD_SUPPORT*/ } else { int subkey_algo; char *curve = NULL; /* Fixme: To support creating a primary key by keygrip we better also define the keyword for the parameter file. Note that the subkey case will never be asserted if a keygrip has been given. */ algo = ask_algo (ctrl, 0, &subkey_algo, &use, NULL); if (subkey_algo) { /* Create primary and subkey at once. */ both = 1; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, both); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; para = r; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { if (algo == PUBKEY_ALGO_EDDSA && subkey_algo == PUBKEY_ALGO_ECDH) { /* Need to switch to a different curve for the encryption key. */ xfree (curve); curve = xstrdup ("Curve25519"); } r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } } else /* Create only a single key. */ { /* For ECC we need to ask for the curve before storing the algo because ask_curve may change the algo. */ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, 0); nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } nbits = 0; } if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { /* The curve has already been set. */ } else { nbits = ask_keysize (both? subkey_algo : algo, nbits); r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } xfree (curve); } expire = ask_expire_interval(0,NULL); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; uid = ask_user_id (0, NULL); if( !uid ) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } r = xmalloc_clear( sizeof *r + strlen(uid) ); r->key = pUSERID; strcpy( r->u.value, uid ); r->next = para; para = r; proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno); release_parameter_list( para ); } #if 0 /* not required */ /* Generate a raw key and return it as a secret key packet. The function will ask for the passphrase and return a protected as well as an unprotected copy of a new secret key packet. 0 is returned on success and the caller must then free the returned values. */ static int generate_raw_key (int algo, unsigned int nbits, u32 created_at, PKT_secret_key **r_sk_unprotected, PKT_secret_key **r_sk_protected) { int rc; DEK *dek = NULL; STRING2KEY *s2k = NULL; PKT_secret_key *sk = NULL; int i; size_t nskey, npkey; gcry_sexp_t s_parms, s_key; int canceled; npkey = pubkey_get_npkey (algo); nskey = pubkey_get_nskey (algo); assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey); if (nbits < 512) { nbits = 512; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info(_("keysize rounded up to %u bits\n"), nbits ); } dek = do_ask_passphrase (&s2k, 1, &canceled); if (canceled) { rc = gpg_error (GPG_ERR_CANCELED); goto leave; } sk = xmalloc_clear (sizeof *sk); sk->timestamp = created_at; sk->version = 4; sk->pubkey_algo = algo; if ( !is_RSA (algo) ) { log_error ("only RSA is supported for offline generated keys\n"); rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } rc = gcry_sexp_build (&s_parms, NULL, "(genkey(rsa(nbits %d)))", (int)nbits); if (rc) log_bug ("gcry_sexp_build failed: %s\n", gpg_strerror (rc)); rc = gcry_pk_genkey (&s_key, s_parms); gcry_sexp_release (s_parms); if (rc) { log_error ("gcry_pk_genkey failed: %s\n", gpg_strerror (rc) ); goto leave; } rc = key_from_sexp (sk->skey, s_key, "private-key", "nedpqu"); gcry_sexp_release (s_key); if (rc) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (rc) ); goto leave; } for (i=npkey; i < nskey; i++) sk->csum += checksum_mpi (sk->skey[i]); if (r_sk_unprotected) *r_sk_unprotected = copy_secret_key (NULL, sk); rc = genhelp_protect (dek, s2k, sk); if (rc) goto leave; if (r_sk_protected) { *r_sk_protected = sk; sk = NULL; } leave: if (sk) free_secret_key (sk); xfree (dek); xfree (s2k); return rc; } #endif /* ENABLE_CARD_SUPPORT */ /* Create and delete a dummy packet to start off a list of kbnodes. */ static void start_tree(KBNODE *tree) { PACKET *pkt; pkt=xmalloc_clear(sizeof(*pkt)); pkt->pkttype=PKT_NONE; *tree=new_kbnode(pkt); delete_kbnode(*tree); } static void do_generate_keypair (struct para_data_s *para, struct output_control_s *outctrl, int card) { gpg_error_t err; KBNODE pub_root = NULL; const char *s; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; struct revocation_key *revkey; int did_sub = 0; u32 timestamp; char *cache_nonce = NULL; if (outctrl->dryrun) { log_info("dry-run mode - key generation skipped\n"); return; } if ( outctrl->use_files ) { if ( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; if (outctrl->pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl->pub.fname); xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; gpg_err_set_errno (EPERM); } else outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0); if (!outctrl->pub.stream) { log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname, strerror(errno) ); return; } if (opt.armor) { outctrl->pub.afx->what = 1; push_armor_filter (outctrl->pub.afx, outctrl->pub.stream); } } assert( outctrl->pub.stream ); if (opt.verbose) log_info (_("writing public key to '%s'\n"), outctrl->pub.fname ); } /* We create the packets as a tree of kbnodes. Because the structure we create is known in advance we simply generate a linked list. The first packet is a dummy packet which we flag as deleted. The very first packet must always be a KEY packet. */ start_tree (&pub_root); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); if (!timestamp) timestamp = make_timestamp (); /* Note that, depending on the backend (i.e. the used scdaemon version), the card key generation may update TIMESTAMP for each key. Thus we need to pass TIMESTAMP to all signing function to make sure that the binding signature is done using the timestamp of the corresponding (sub)key and not that of the primary key. An alternative implementation could tell the signing function the node of the subkey but that is more work than just to pass the current timestamp. */ if (!card) err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ), get_parameter_uint( para, pKEYLENGTH ), get_parameter_value (para, pKEYCURVE), pub_root, timestamp, get_parameter_u32( para, pKEYEXPIRE ), 0, outctrl->keygen_flags, &cache_nonce); else err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); /* Get the pointer to the generated public key packet. */ if (!err) { pri_psk = pub_root->next->pkt->pkt.public_key; assert (pri_psk); } if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) err = write_direct_sig (pub_root, pri_psk, revkey, timestamp, cache_nonce); if (!err && (s = get_parameter_value (para, pUSERID))) { write_uid (pub_root, s ); err = write_selfsigs (pub_root, pri_psk, get_parameter_uint (para, pKEYUSAGE), timestamp, cache_nonce); } /* Write the auth key to the card before the encryption key. This is a partial workaround for a PGP bug (as of this writing, all versions including 8.1), that causes it to try and encrypt to the most recent subkey regardless of whether that subkey is actually an encryption type. In this case, the auth key is an RSA key so it succeeds. */ if (!err && card && get_parameter (para, pAUTHKEYTYPE)) { err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); if (!err) err = write_keybinding (pub_root, pri_psk, NULL, PUBKEY_USAGE_AUTH, timestamp, cache_nonce); } if (!err && get_parameter (para, pSUBKEYTYPE)) { sub_psk = NULL; if (!card) { err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL), get_parameter_uint (para, pSUBKEYLENGTH), get_parameter_value (para, pSUBKEYCURVE), pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, outctrl->keygen_flags, &cache_nonce); /* Get the pointer to the generated public subkey packet. */ if (!err) { kbnode_t node; for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; assert (sub_psk); } } else { if ((s = get_parameter_value (para, pCARDBACKUPKEY))) { /* A backup of the encryption key has been requested. Generate the key in software and import it then to the card. Write a backup file. */ err = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0, pub_root, timestamp, get_parameter_u32 (para, pKEYEXPIRE), para); } else { err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, ×tamp, get_parameter_u32 (para, pKEYEXPIRE)); } } if (!err) err = write_keybinding (pub_root, pri_psk, sub_psk, get_parameter_uint (para, pSUBKEYUSAGE), timestamp, cache_nonce); did_sub = 1; } if (!err && outctrl->use_files) /* Direct write to specified files. */ { err = write_keyblock (outctrl->pub.stream, pub_root); if (err) log_error ("can't write public key: %s\n", g10_errstr (err)); } else if (!err) /* Write to the standard keyrings. */ { KEYDB_HANDLE pub_hd = keydb_new (); err = keydb_locate_writable (pub_hd, NULL); if (err) log_error (_("no writable public keyring found: %s\n"), g10_errstr (err)); if (!err && opt.verbose) { log_info (_("writing public key to '%s'\n"), keydb_get_resource_name (pub_hd)); } if (!err) { err = keydb_insert_keyblock (pub_hd, pub_root); if (err) log_error (_("error writing public keyring '%s': %s\n"), keydb_get_resource_name (pub_hd), g10_errstr(err)); } keydb_release (pub_hd); if (!err) { int no_enc_rsa; PKT_public_key *pk; no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_RSA) && get_parameter_uint (para, pKEYUSAGE) && !((get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC)) ); pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; keyid_from_pk (pk, pk->main_keyid); register_trusted_keyid (pk->main_keyid); update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); - gen_standard_revoke (pk); + gen_standard_revoke (pk, cache_nonce); if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); tty_printf ("\n"); list_keyblock (pub_root, 0, 1, 1, NULL); } if (!opt.batch && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA || no_enc_rsa ) && !get_parameter (para, pSUBKEYTYPE) ) { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "subkey for this purpose.\n") ); } } } if (err) { if (opt.batch) log_error ("key generation failed: %s\n", g10_errstr(err) ); else tty_printf (_("Key generation failed: %s\n"), g10_errstr(err) ); write_status_error (card? "card_key_generate":"key_generate", err); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else { PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } release_kbnode (pub_root); xfree (cache_nonce); } /* Add a new subkey to an existing key. Returns 0 if a new key has been generated and put into the keyblocks. */ gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; int algo; unsigned int use; u32 expire; unsigned int nbits = 0; char *curve = NULL; u32 cur_time; char *hexgrip = NULL; char *serialno = NULL; /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; primary key missing in keyblock!\n"); err = gpg_error (GPG_ERR_BUG); goto leave; } pri_psk = node->pkt->pkt.public_key; cur_time = make_timestamp (); if (pri_psk->timestamp > cur_time) { ulong d = pri_psk->timestamp - cur_time; log_info ( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_psk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_CONFLICT); goto leave; } err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; if (agent_get_keyinfo (NULL, hexgrip, &serialno)) { tty_printf (_("Secret parts of primary key are not available.\n")); goto leave; } if (serialno) tty_printf (_("Secret parts of primary key are stored on-card.\n")); xfree (hexgrip); hexgrip = NULL; algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip); assert (algo); if (hexgrip) nbits = 0; else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) curve = ask_curve (&algo, 0); else nbits = ask_keysize (algo, 0); expire = ask_expire_interval (0, NULL); if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } if (hexgrip) err = do_create_from_keygrip (ctrl, algo, hexgrip, keyblock, cur_time, expire, 1); else err = do_create (algo, nbits, curve, keyblock, cur_time, expire, 1, 0, NULL); if (err) goto leave; /* Get the pointer to the generated public subkey packet. */ for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; /* Write the binding signature. */ err = write_keybinding (keyblock, pri_psk, sub_psk, use, cur_time, NULL); if (err) goto leave; write_status_text (STATUS_KEY_CREATED, "S"); leave: xfree (curve); xfree (hexgrip); xfree (serialno); if (err) log_error (_("Key generation failed: %s\n"), g10_errstr (err) ); return err; } #ifdef ENABLE_CARD_SUPPORT /* Generate a subkey on a card. */ gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int keyno, const char *serialno) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_pk = NULL; int algo; unsigned int use; u32 expire; u32 cur_time; struct para_data_s *para = NULL; assert (keyno >= 1 && keyno <= 3); para = xtrycalloc (1, sizeof *para + strlen (serialno) ); if (!para) { err = gpg_error_from_syserror (); goto leave; } para->key = pSERIALNO; strcpy (para->u.value, serialno); /* Break out the primary secret key */ node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; publkic key lost!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pri_pk = node->pkt->pkt.public_key; cur_time = make_timestamp(); if (pri_pk->timestamp > cur_time) { ulong d = pri_pk->timestamp - cur_time; log_info (d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_pk->version < 4) { log_info (_("NOTE: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } algo = PUBKEY_ALGO_RSA; expire = ask_expire_interval (0, NULL); if (keyno == 1) use = PUBKEY_USAGE_SIG; else if (keyno == 2) use = PUBKEY_USAGE_ENC; else use = PUBKEY_USAGE_AUTH; if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } /* Note, that depending on the backend, the card key generation may update CUR_TIME. */ err = gen_card_key (algo, keyno, 0, pub_keyblock, &cur_time, expire); /* Get the pointer to the generated public subkey packet. */ if (!err) { PKT_public_key *sub_pk = NULL; for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; assert (sub_pk); err = write_keybinding (pub_keyblock, pri_pk, sub_pk, use, cur_time, NULL); } leave: if (err) log_error (_("Key generation failed: %s\n"), g10_errstr(err) ); else write_status_text (STATUS_KEY_CREATED, "S"); release_parameter_list (para); return err; } #endif /* !ENABLE_CARD_SUPPORT */ /* * Write a keyblock to an output stream */ static int write_keyblock( IOBUF out, KBNODE node ) { for( ; node ; node = node->next ) { if(!is_deleted_kbnode(node)) { int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, g10_errstr(rc) ); return rc; } } } return 0; } /* Note that timestamp is an in/out arg. */ static gpg_error_t gen_card_key (int algo, int keyno, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval) { #ifdef ENABLE_CARD_SUPPORT gpg_error_t err; struct agent_card_genkey_s info; PACKET *pkt; PKT_public_key *pk; if (algo != PUBKEY_ALGO_RSA) return gpg_error (GPG_ERR_PUBKEY_ALGO); pk = xtrycalloc (1, sizeof *pk ); if (!pk) return gpg_error_from_syserror (); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { xfree (pk); return gpg_error_from_syserror (); } /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ err = agent_scd_genkey (&info, keyno, 1, NULL, *timestamp); /* The code below is not used because we force creation of * the a card key (3rd arg). * if (gpg_err_code (rc) == GPG_ERR_EEXIST) * { * tty_printf ("\n"); * log_error ("WARNING: key does already exists!\n"); * tty_printf ("\n"); * if ( cpr_get_answer_is_yes( "keygen.card.replace_key", * _("Replace existing key? "))) * rc = agent_scd_genkey (&info, keyno, 1); * } */ if (!err && (!info.n || !info.e)) { log_error ("communication error with SCD\n"); gcry_mpi_release (info.n); gcry_mpi_release (info.e); err = gpg_error (GPG_ERR_GENERAL); } if (err) { log_error ("key generation failed: %s\n", gpg_strerror (err)); xfree (pkt); xfree (pk); return err; } if (*timestamp != info.created_at) log_info ("NOTE: the key does not use the suggested creation date\n"); *timestamp = info.created_at; pk->timestamp = info.created_at; pk->version = 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; pk->pkey[0] = info.n; pk->pkey[1] = info.e; pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; #else (void)algo; (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ } static int gen_card_key_with_backup (int algo, int keyno, int is_primary, KBNODE pub_root, u32 timestamp, u32 expireval, struct para_data_s *para) { #if ENABLE_CARD_SUPPORT && 0 /* FIXME: Move this to gpg-agent. */ int rc; const char *s; PACKET *pkt; PKT_secret_key *sk, *sk_unprotected = NULL, *sk_protected = NULL; PKT_public_key *pk; size_t n; int i; unsigned int nbits; /* Get the size of the key directly from the card. */ { struct agent_card_info_s info; memset (&info, 0, sizeof info); if (!agent_scd_getattr ("KEY-ATTR", &info) && info.key_attr[1].algo) nbits = info.key_attr[1].nbits; else nbits = 1024; /* All pre-v2.0 cards. */ agent_release_card_info (&info); } /* Create a key of this size in memory. */ rc = generate_raw_key (algo, nbits, timestamp, &sk_unprotected, &sk_protected); if (rc) return rc; /* Store the key to the card. */ rc = save_unprotected_key_to_card (sk_unprotected, keyno); if (rc) { log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc)); free_secret_key (sk_unprotected); free_secret_key (sk_protected); write_status_errcode ("save_key_to_card", rc); return rc; } /* Get rid of the secret key parameters and store the serial numer. */ sk = sk_unprotected; n = pubkey_get_nskey (sk->pubkey_algo); for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) { gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL; } i = pubkey_get_npkey (sk->pubkey_algo); sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8); sk->is_protected = 1; sk->protect.s2k.mode = 1002; s = get_parameter_value (para, pSERIALNO); assert (s); for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1]; sk->protect.ivlen++, s += 2) sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s); /* Now write the *protected* secret key to the file. */ { char name_buffer[50]; char *fname; IOBUF fp; mode_t oldmask; keyid_from_sk (sk, NULL); snprintf (name_buffer, sizeof name_buffer, "sk_%08lX%08lX.gpg", (ulong)sk->keyid[0], (ulong)sk->keyid[1]); fname = make_filename (backup_dir, name_buffer, NULL); /* Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp = iobuf_create (fname, 1); umask (oldmask); if (!fp) { rc = gpg_error_from_syserror (); log_error (_("can't create backup file '%s': %s\n"), fname, strerror(errno) ); xfree (fname); free_secret_key (sk_unprotected); free_secret_key (sk_protected); return rc; } pkt = xcalloc (1, sizeof *pkt); pkt->pkttype = PKT_SECRET_KEY; pkt->pkt.secret_key = sk_protected; sk_protected = NULL; rc = build_packet (fp, pkt); if (rc) { log_error("build packet failed: %s\n", g10_errstr(rc) ); iobuf_cancel (fp); } else { unsigned char array[MAX_FINGERPRINT_LEN]; char *fprbuf, *p; iobuf_close (fp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); log_info (_("NOTE: backup of card key saved to '%s'\n"), fname); fingerprint_from_sk (sk, array, &n); p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1); for (i=0; i < n ; i++, p += 2) sprintf (p, "%02X", array[i]); *p++ = ' '; *p = 0; write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, fname, strlen (fname), 0); xfree (fprbuf); } free_packet (pkt); xfree (pkt); xfree (fname); if (rc) { free_secret_key (sk_unprotected); return rc; } } /* Create the public key from the secret key. */ pk = xcalloc (1, sizeof *pk ); pk->timestamp = sk->timestamp; pk->version = sk->version; if (expireval) pk->expiredate = sk->expiredate = sk->timestamp + expireval; pk->pubkey_algo = sk->pubkey_algo; n = pubkey_get_npkey (sk->pubkey_algo); for (i=0; i < n; i++) pk->pkey[i] = mpi_copy (sk->skey[i]); /* Build packets and add them to the node lists. */ pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode(pub_root, new_kbnode( pkt )); pkt = xcalloc (1,sizeof *pkt); pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; pkt->pkt.secret_key = sk; add_kbnode(sec_root, new_kbnode( pkt )); return 0; #else # if __GCC__ && ENABLE_CARD_SUPPORT # warning Card support still missing # endif (void)algo; (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; (void)para; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ } #if 0 int save_unprotected_key_to_card (PKT_public_key *sk, int keyno) { int rc; unsigned char *rsa_n = NULL; unsigned char *rsa_e = NULL; unsigned char *rsa_p = NULL; unsigned char *rsa_q = NULL; size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len; unsigned char *sexp = NULL; unsigned char *p; char numbuf[55], numbuf2[50]; assert (is_RSA (sk->pubkey_algo)); assert (!sk->is_protected); /* Copy the parameters into straight buffers. */ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_n, &rsa_n_len, sk->skey[0]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_e, &rsa_e_len, sk->skey[1]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_p, &rsa_p_len, sk->skey[3]); gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_q, &rsa_q_len, sk->skey[4]); if (!rsa_n || !rsa_e || !rsa_p || !rsa_q) { rc = G10ERR_INV_ARG; goto leave; } /* Put the key into an S-expression. */ sexp = p = xmalloc_secure (30 + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20); p = stpcpy (p,"(11:private-key(3:rsa(1:n"); sprintf (numbuf, "%u:", (unsigned int)rsa_n_len); p = stpcpy (p, numbuf); memcpy (p, rsa_n, rsa_n_len); p += rsa_n_len; sprintf (numbuf, ")(1:e%u:", (unsigned int)rsa_e_len); p = stpcpy (p, numbuf); memcpy (p, rsa_e, rsa_e_len); p += rsa_e_len; sprintf (numbuf, ")(1:p%u:", (unsigned int)rsa_p_len); p = stpcpy (p, numbuf); memcpy (p, rsa_p, rsa_p_len); p += rsa_p_len; sprintf (numbuf, ")(1:q%u:", (unsigned int)rsa_q_len); p = stpcpy (p, numbuf); memcpy (p, rsa_q, rsa_q_len); p += rsa_q_len; p = stpcpy (p,"))(10:created-at"); sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp); sprintf (numbuf, "%lu:", (unsigned long)strlen (numbuf2)); p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))"); /* Fixme: Unfortunately we don't have the serialnumber available - thus we can't pass it down to the agent. */ rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp); leave: xfree (sexp); xfree (rsa_n); xfree (rsa_e); xfree (rsa_p); xfree (rsa_q); return rc; } #endif /*ENABLE_CARD_SUPPORT*/ diff --git a/g10/main.h b/g10/main.h index 4eb1b5f31..44c447866 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,405 +1,405 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "types.h" #include "iobuf.h" #include "keydb.h" #include "util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #if GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif #define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1 #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ typedef struct { int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; strlist_t values; struct groupitem *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_md5_rejected_note (void); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); int disable_core_dumps(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; char *pct_expando(const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); void obsolete_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); int default_compress_algo(void); const char *compliance_option_string(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); int has_invalid_email_chars (const char *s); int is_valid_mailbox (const char *name); int is_valid_user_id (const char *uid); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- status.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_text ( int no, const char *text ); void write_status_strings (int no, const char *text, ...) GNUPG_GCC_A_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); /*-- sign.c --*/ int complete_sig (PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md, const char *cache_nonce); int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file( const char *fname, strlist_t locusr, const char *outfile ); int sign_symencrypt_file (const char *fname, strlist_t locusr); /*-- sig-check.c --*/ int check_revocation_keys (PKT_public_key *pk, PKT_signature *sig); int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig ); int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired ); /*-- delkey.c --*/ gpg_error_t delete_keys (strlist_t names, int secret, int allow_both); /*-- keyedit.c --*/ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check ); void keyedit_passwd (ctrl_t ctrl, const char *username); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local); void show_basic_key_info (KBNODE keyblock); /*-- keygen.c --*/ u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); void quick_generate_keypair (const char *uid); void generate_keypair (ctrl_t ctrl, const char *fname, const char *card_serialno, int card_backup_key); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (kbnode_t pub_keyblock, int keyno, const char *serialno); int save_unprotected_key_to_card (PKT_public_key *sk, int keyno); #endif /*-- openfile.c --*/ 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 inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); iobuf_t open_sigfile( const char *iname, progress_filter_context_t *pfx ); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); void import_keys (ctrl_t ctrl, char **fnames, int nnames, void *stats_hd, unsigned int options); int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd, unsigned char **fpr, size_t *fpr_len, unsigned int options); int import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); void *import_new_stats_handle (void); void import_release_stats_handle (void *p); void import_print_stats (void *hd); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ int parse_export_options(char *str,unsigned int *options,int noisy); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options ); int export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, kbnode_t *keyblock_out, unsigned int options ); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); int export_seckeys (ctrl_t ctrl, strlist_t users); int export_secsubkeys (ctrl_t ctrl, strlist_t users); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; -int gen_standard_revoke (PKT_public_key *psk); +int gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce); int gen_revoke( const char *uname ); int gen_desig_revoke( const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode ); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock (kbnode_t keyblock, int secret, int has_secret, int fpr, void *opaque); void print_fingerprint (estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); void print_seckey_info (PKT_public_key *pk); void print_pubkey_info (estream_t fp, PKT_public_key *pk); void print_card_key_info (estream_t fp, KBNODE keyblock); /*-- verify.c --*/ 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); /*-- 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); 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 ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- signal.c --*/ void init_signals(void); void block_all_signals(void); void unblock_all_signals(void); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (estream_t fp, char *serialno, size_t serialnobuflen); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (KBNODE pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/ diff --git a/g10/revoke.c b/g10/revoke.c index 67f62e5cc..019c62c0a 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,808 +1,810 @@ /* revoke.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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 a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "status.h" #include "keydb.h" #include "util.h" #include "main.h" #include "ttyio.h" #include "status.h" #include "i18n.h" #include "call-agent.h" struct revocation_reason_info { int code; char *desc; }; int revocation_reason_build_cb( PKT_signature *sig, void *opaque ) { struct revocation_reason_info *reason = opaque; char *ud = NULL; byte *buffer; size_t buflen = 1; if(!reason) return 0; if( reason->desc ) { ud = native_to_utf8( reason->desc ); buflen += strlen(ud); } buffer = xmalloc( buflen ); *buffer = reason->code; if( ud ) { memcpy(buffer+1, ud, strlen(ud) ); xfree( ud ); } build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); xfree( buffer ); return 0; } /* Outputs a minimal pk (as defined by 2440) from a keyblock. A minimal pk consists of the public key packet and a user ID. We try and pick a user ID that has a uid signature, and include it if possible. */ static int export_minimal_pk(IOBUF out,KBNODE keyblock, PKT_signature *revsig,PKT_signature *revkey) { KBNODE node; PACKET pkt; PKT_user_id *uid=NULL; PKT_signature *selfsig=NULL; u32 keyid[2]; int rc; node=find_kbnode(keyblock,PKT_PUBLIC_KEY); if(!node) { log_error("key incomplete\n"); return G10ERR_GENERAL; } keyid_from_pk(node->pkt->pkt.public_key,keyid); pkt=*node->pkt; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } init_packet(&pkt); pkt.pkttype=PKT_SIGNATURE; /* the revocation itself, if any. 2440 likes this to come first. */ if(revsig) { pkt.pkt.signature=revsig; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } /* If a revkey in a 1F sig is present, include it too */ if(revkey) { pkt.pkt.signature=revkey; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } while(!selfsig) { KBNODE signode; node=find_next_kbnode(node,PKT_USER_ID); if(!node) { /* We're out of user IDs - none were self-signed. */ if(uid) break; else { log_error(_("key %s has no user IDs\n"),keystr(keyid)); return G10ERR_GENERAL; } } if(node->pkt->pkt.user_id->attrib_data) continue; uid=node->pkt->pkt.user_id; signode=node; while((signode=find_next_kbnode(signode,PKT_SIGNATURE))) { if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_UID_SIG(signode->pkt->pkt.signature)) { selfsig=signode->pkt->pkt.signature; break; } } } pkt.pkttype=PKT_USER_ID; pkt.pkt.user_id=uid; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } if(selfsig) { pkt.pkttype=PKT_SIGNATURE; pkt.pkt.signature=selfsig; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), g10_errstr(rc) ); return rc; } } return 0; } /**************** * Generate a revocation certificate for UNAME via a designated revoker */ int gen_desig_revoke( const char *uname, strlist_t locusr ) { int rc = 0; armor_filter_context_t *afx; PKT_public_key *pk = NULL; PKT_public_key *pk2 = NULL; PKT_signature *sig = NULL; IOBUF out = NULL; struct revocation_reason_info *reason = NULL; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; KBNODE keyblock=NULL,node; u32 keyid[2]; int i,any=0; SK_LIST sk_list=NULL; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return G10ERR_GENERAL; } afx = new_armor_context (); kdbhd = keydb_new (); rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { log_error (_("key \"%s\" not found: %s\n"),uname, g10_errstr (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } /* To parse the revkeys */ merge_keys_and_selfsig(keyblock); /* get the key from the keyblock */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if( !node ) BUG (); pk=node->pkt->pkt.public_key; keyid_from_pk(pk,keyid); if(locusr) { rc=build_sk_list(locusr, &sk_list, PUBKEY_USAGE_CERT); if(rc) goto leave; } /* Are we a designated revoker for this key? */ if(!pk->revkey && pk->numrevkeys) BUG(); for(i=0;inumrevkeys;i++) { SK_LIST list; free_public_key (pk2); pk2 = NULL; if(sk_list) { for(list=sk_list;list;list=list->next) { byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; fingerprint_from_pk (list->pk, fpr, &fprlen); /* Don't get involved with keys that don't have 160 bit fingerprints */ if(fprlen!=20) continue; if(memcmp(fpr,pk->revkey[i].fpr,20)==0) break; } if (list) pk2 = copy_public_key (NULL, list->pk); else continue; } else { pk2 = xmalloc_clear (sizeof *pk2); rc = get_pubkey_byfprint (pk2, pk->revkey[i].fpr, MAX_FINGERPRINT_LEN); } /* We have the revocation key. */ if(!rc) { PKT_signature *revkey = NULL; any = 1; print_pubkey_info (NULL, pk); tty_printf ("\n"); tty_printf (_("To be revoked by:\n")); print_seckey_info (pk2); if(pk->revkey[i].class&0x40) tty_printf(_("(This is a sensitive revocation key)\n")); tty_printf("\n"); if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", _("Create a designated revocation certificate for this key? (y/N) "))) continue; /* get the reason for the revocation (this is always v4) */ reason = ask_revocation_reason( 1, 0, 1 ); if( !reason ) continue; rc = -1;/*FIXME: check_secret_key (pk2, 0 );*/ if (rc) continue; if( !opt.armor ) tty_printf(_("ASCII armored output forced.\n")); if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) goto leave; afx->what = 1; afx->hdrlines = "Comment: A designated revocation certificate" " should follow\n"; push_armor_filter (afx, out); /* create it */ rc = make_keysig_packet( &sig, pk, NULL, NULL, pk2, 0x20, 0, 0, 0, 0, revocation_reason_build_cb, reason, NULL); if( rc ) { log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc)); goto leave; } /* Spit out a minimal pk as well, since otherwise there is no way to know which key to attach this revocation to. Also include the direct key signature that contains this revocation key. We're allowed to include sensitive revocation keys along with a revocation, as this may be the only time the recipient has seen it. Note that this means that if we have multiple different sensitive revocation keys in a given direct key signature, we're going to include them all here. This is annoying, but the good outweighs the bad, since without including this a sensitive revoker can't really do their job. People should not include multiple sensitive revocation keys in one signature: 2440 says "Note that it may be appropriate to isolate this subpacket within a separate signature so that it is not combined with other subpackets that need to be exported." -dms */ while(!revkey) { KBNODE signode; signode=find_next_kbnode(node,PKT_SIGNATURE); if(!signode) break; node=signode; if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_KEY_SIG(signode->pkt->pkt.signature)) { int j; for(j=0;jpkt->pkt.signature->numrevkeys;j++) { if(pk->revkey[i].class== signode->pkt->pkt.signature->revkey[j]->class && pk->revkey[i].algid== signode->pkt->pkt.signature->revkey[j]->algid && memcmp(pk->revkey[i].fpr, signode->pkt->pkt.signature->revkey[j]->fpr, MAX_FINGERPRINT_LEN)==0) { revkey=signode->pkt->pkt.signature; break; } } } } if(!revkey) BUG(); rc=export_minimal_pk(out,keyblock,sig,revkey); if(rc) goto leave; /* and issue a usage notice */ tty_printf(_("Revocation certificate created.\n")); break; } } if(!any) log_error(_("no revocation keys found for \"%s\"\n"),uname); leave: free_public_key (pk); free_public_key (pk2); if( sig ) free_seckey_enc( sig ); release_sk_list(sk_list); if( rc ) iobuf_cancel(out); else iobuf_close(out); release_revocation_reason_info( reason ); release_armor_context (afx); return rc; } /* Common core to create the revocation. FILENAME may be NULL to write to stdout or the filename given by --output. REASON describes the revocation reason. PSK is the public primary key - we expect that a corresponding secret key is available. KEYBLOCK is the entire KEYBLOCK which is used in PGP mode to write a a minimal key and not just the naked revocation signature; it may be NULL. If LEADINTEXT is not NULL, it is written right before the (armored) output.*/ static int create_revocation (const char *filename, struct revocation_reason_info *reason, PKT_public_key *psk, kbnode_t keyblock, - const char *leadintext, int suffix) + const char *leadintext, int suffix, + const char *cache_nonce) { int rc; iobuf_t out = NULL; armor_filter_context_t *afx; PKT_signature *sig = NULL; PACKET pkt; afx = new_armor_context (); if ((rc = open_outfile (-1, filename, suffix, 1, &out))) goto leave; if (leadintext ) iobuf_writestr (out, leadintext); afx->what = 1; afx->hdrlines = "Comment: This is a revocation certificate\n"; push_armor_filter (afx, out); rc = make_keysig_packet (&sig, psk, NULL, NULL, psk, 0x20, 0, opt.force_v4_certs? 4:0, 0, 0, - revocation_reason_build_cb, reason, NULL); + revocation_reason_build_cb, reason, cache_nonce); if (rc) { log_error (_("make_keysig_packet failed: %s\n"), g10_errstr (rc)); goto leave; } if (keyblock && (PGP6 || PGP7 || PGP8)) { /* Use a minimal pk for PGPx mode, since PGP can't import bare revocation certificates. */ rc = export_minimal_pk (out, keyblock, sig, NULL); if (rc) goto leave; } else { init_packet (&pkt); pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet (out, &pkt); if (rc) { log_error (_("build_packet failed: %s\n"), g10_errstr (rc)); goto leave; } } leave: if (sig) free_seckey_enc (sig); if (rc) iobuf_cancel (out); else iobuf_close (out); release_armor_context (afx); return rc; } /* This function is used to generate a standard revocation certificate by gpg's interactive key generation function. The certificate is stored at a dedicated place in a slightly modified form to avoid an accidental import. PSK is the primary key; a corresponding secret - key must be available. */ + key must be available. CACHE_NONCE is optional but can be used to + help gpg-agent to avoid an extra passphrase prompt. */ int -gen_standard_revoke (PKT_public_key *psk) +gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce) { int rc; estream_t memfp; struct revocation_reason_info reason; char *dir, *tmpstr, *fname; void *leadin; size_t len; u32 keyid[2]; char pkstrbuf[PUBKEY_STRING_SIZE]; char *orig_codeset; dir = get_openpgp_revocdir (opt.homedir); tmpstr = hexfingerprint (psk); fname = xstrconcat (dir, DIRSEP_S, tmpstr, NULL); xfree (tmpstr); xfree (dir); keyid_from_pk (psk, keyid); memfp = es_fopenmem (0, "r+"); if (!memfp) log_fatal ("error creating memory stream\n"); orig_codeset = i18n_switchto_utf8 (); es_fprintf (memfp, "%s\n\n", _("This is a revocation certificate for the OpenPGP key:")); es_fprintf (memfp, "pub %s/%s %s\n", pubkey_string (psk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (psk)); print_fingerprint (memfp, psk, 3); tmpstr = get_user_id (keyid, &len); es_fprintf (memfp, "uid%*s%.*s\n\n", (int)keystrlen () + 10, "", (int)len, tmpstr); xfree (tmpstr); es_fprintf (memfp, "%s\n\n%s\n\n:", _("Use it to revoke this key in case of a compromise or loss of\n" "the secret key. However, if the secret key is still accessible,\n" "it is better to generate a new revocation certificate and give\n" "a reason for the revocation."), _("To avoid an accidental use of this file, a colon has been inserted\n" "before the 5 dashes below. Remove this colon with a text editor\n" "before making use of this revocation certificate.")); es_putc (0, memfp); i18n_switchback (orig_codeset); if (es_fclose_snatch (memfp, &leadin, NULL)) log_fatal ("error snatching memory stream\n"); reason.code = 0x00; /* No particular reason. */ reason.desc = NULL; - rc = create_revocation (fname, &reason, psk, NULL, leadin, 3); + rc = create_revocation (fname, &reason, psk, NULL, leadin, 3, cache_nonce); xfree (leadin); xfree (fname); return rc; } /**************** * Generate a revocation certificate for UNAME */ int gen_revoke (const char *uname) { int rc = 0; PKT_public_key *psk; u32 keyid[2]; kbnode_t keyblock = NULL; kbnode_t node; KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; KEYDB_SEARCH_DESC desc; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return G10ERR_GENERAL; } /* Search the userid; we don't want the whole getkey stuff here. */ kdbhd = keydb_new (); rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { log_error (_("secret key \"%s\" not found: %s\n"), uname, g10_errstr (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if (rc) { log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); goto leave; } /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) BUG (); psk = node->pkt->pkt.public_key; rc = agent_probe_secret_key (NULL, psk); if (rc) { log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; } keyid_from_pk (psk, keyid ); print_seckey_info (psk); tty_printf("\n"); if (!cpr_get_answer_is_yes ("gen_revoke.okay", _("Create a revocation certificate for this key? (y/N) "))) { rc = 0; goto leave; } if (psk->version >= 4 || opt.force_v4_certs) { /* Get the reason for the revocation. */ reason = ask_revocation_reason (1, 0, 1); if (!reason) { /* user decided to cancel */ rc = 0; goto leave; } } if (!opt.armor) tty_printf (_("ASCII armored output forced.\n")); - rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0); + rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0, NULL); if (rc) goto leave; /* and issue a usage notice */ tty_printf (_( "Revocation certificate created.\n\n" "Please move it to a medium which you can hide away; if Mallory gets\n" "access to this certificate he can use it to make your key unusable.\n" "It is smart to print this certificate and store it away, just in case\n" "your media become unreadable. But have some caution: The print system of\n" "your machine might store the data and make it available to others!\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); release_revocation_reason_info( reason ); return rc; } struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ) { int code=-1; char *description = NULL; struct revocation_reason_info *reason; const char *text_0 = _("No reason specified"); const char *text_1 = _("Key has been compromised"); const char *text_2 = _("Key is superseded"); const char *text_3 = _("Key is no longer used"); const char *text_4 = _("User ID is no longer valid"); const char *code_text = NULL; do { code=-1; xfree(description); description = NULL; tty_printf(_("Please select the reason for the revocation:\n")); tty_printf( " 0 = %s\n", text_0 ); if( key_rev ) tty_printf(" 1 = %s\n", text_1 ); if( key_rev ) tty_printf(" 2 = %s\n", text_2 ); if( key_rev ) tty_printf(" 3 = %s\n", text_3 ); if( cert_rev ) tty_printf(" 4 = %s\n", text_4 ); tty_printf( " Q = %s\n", _("Cancel") ); if( hint ) tty_printf(_("(Probably you want to select %d here)\n"), hint ); while(code==-1) { int n; char *answer = cpr_get("ask_revocation_reason.code", _("Your decision? ")); trim_spaces( answer ); cpr_kill_prompt(); if( *answer == 'q' || *answer == 'Q') return NULL; /* cancel */ if( hint && !*answer ) n = hint; else if(!digitp( answer ) ) n = -1; else n = atoi(answer); xfree(answer); if( n == 0 ) { code = 0x00; /* no particular reason */ code_text = text_0; } else if( key_rev && n == 1 ) { code = 0x02; /* key has been compromised */ code_text = text_1; } else if( key_rev && n == 2 ) { code = 0x01; /* key is superseded */ code_text = text_2; } else if( key_rev && n == 3 ) { code = 0x03; /* key is no longer used */ code_text = text_3; } else if( cert_rev && n == 4 ) { code = 0x20; /* uid is no longer valid */ code_text = text_4; } else tty_printf(_("Invalid selection.\n")); } tty_printf(_("Enter an optional description; " "end it with an empty line:\n") ); for(;;) { char *answer = cpr_get("ask_revocation_reason.text", "> " ); trim_trailing_ws( answer, strlen(answer) ); cpr_kill_prompt(); if( !*answer ) { xfree(answer); break; } { char *p = make_printable_string( answer, strlen(answer), 0 ); xfree(answer); answer = p; } if( !description ) description = xstrdup(answer); else { char *p = xmalloc( strlen(description) + strlen(answer) + 2 ); strcpy(stpcpy(stpcpy( p, description),"\n"),answer); xfree(description); description = p; } xfree(answer); } tty_printf(_("Reason for revocation: %s\n"), code_text ); if( !description ) tty_printf(_("(No description given)\n") ); else tty_printf("%s\n", description ); } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", _("Is this okay? (y/N) ")) ); reason = xmalloc( sizeof *reason ); reason->code = code; reason->desc = description; return reason; } void release_revocation_reason_info( struct revocation_reason_info *reason ) { if( reason ) { xfree( reason->desc ); xfree( reason ); } }