diff options
-rw-r--r-- | agent/agent.h | 5 | ||||
-rw-r--r-- | agent/command-ssh.c | 5 | ||||
-rw-r--r-- | agent/command.c | 67 | ||||
-rw-r--r-- | agent/cvt-openpgp.c | 4 | ||||
-rw-r--r-- | agent/findkey.c | 36 | ||||
-rw-r--r-- | agent/genkey.c | 16 | ||||
-rw-r--r-- | agent/keyformat.txt | 36 | ||||
-rw-r--r-- | agent/protect-tool.c | 4 |
8 files changed, 138 insertions, 35 deletions
diff --git a/agent/agent.h b/agent/agent.h index 5bec5e51d..4d981adcf 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -413,7 +413,8 @@ void start_command_handler_ssh (ctrl_t, gnupg_fd_t); gpg_error_t agent_modify_description (const char *in, const char *comment, const gcry_sexp_t key, char **result); int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force); + const void *buffer, size_t length, int force, + time_t timestamp); gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, @@ -493,7 +494,7 @@ int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty, char **failed_constraint); 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, +int agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp, const char *keyparam, size_t keyparmlen, int no_protection, const char *override_passphrase, int preset, membuf_t *outbuf); diff --git a/agent/command-ssh.c b/agent/command-ssh.c index d6729b70a..a26d60417 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -3136,8 +3136,9 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec, if (err) goto out; - /* Store this key to our key storage. */ - err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0); + /* Store this key to our key storage. We do not store a creation + * timestamp because we simply do not know. */ + err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, 0); if (err) goto out; diff --git a/agent/command.c b/agent/command.c index 2844a9e64..9e3483e8c 100644 --- a/agent/command.c +++ b/agent/command.c @@ -835,8 +835,8 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) static const char hlp_genkey[] = - "GENKEY [--no-protection] [--preset] [--inq-passwd]\n" - " [--passwd-nonce=<s>] [<cache_nonce>]\n" + "GENKEY [--no-protection] [--preset] [--timestamp=<isodate>]\n" + " [--inq-passwd] [--passwd-nonce=<s>] [<cache_nonce>]\n" "\n" "Generate a new key, store the secret part and return the public\n" "part. Here is an example transaction:\n" @@ -849,11 +849,13 @@ static const char hlp_genkey[] = " S: D (rsa (n 326487324683264) (e 10001)))\n" " S: OK key created\n" "\n" - "When the --preset option is used the passphrase for the generated\n" - "key will be added to the cache. When --inq-passwd is used an inquire\n" + "If the --preset option is used the passphrase for the generated\n" + "key will be added to the cache. If --inq-passwd is used an inquire\n" "with the keyword NEWPASSWD is used to request the passphrase for the\n" - "new key. When a --passwd-nonce is used, the corresponding cached\n" - "passphrase is used to protect the new key."; + "new key. If a --passwd-nonce is used, the corresponding cached\n" + "passphrase is used to protect the new key. If --timestamp is given\n" + "its value is recorded as the key's creation time; the value is\n" + "expected in ISO format (e.g. \"20030316T120000\")."; static gpg_error_t cmd_genkey (assuan_context_t ctx, char *line) { @@ -870,6 +872,8 @@ cmd_genkey (assuan_context_t ctx, char *line) int opt_inq_passwd; size_t n; char *p, *pend; + const char *s; + time_t opt_timestamp; int c; if (ctrl->restricted) @@ -893,6 +897,22 @@ cmd_genkey (assuan_context_t ctx, char *line) goto leave; } } + if ((s=has_option_name (line, "--timestamp"))) + { + if (*s != '=') + { + rc = set_error (GPG_ERR_ASS_PARAMETER, "missing value for option"); + goto leave; + } + opt_timestamp = isotime2epoch (s+1); + if (opt_timestamp < 1) + { + rc = set_error (GPG_ERR_ASS_PARAMETER, "invalid time value"); + goto leave; + } + } + else + opt_timestamp = 0; line = skip_options (line); for (p=line; *p && *p != ' ' && *p != '\t'; p++) @@ -932,7 +952,8 @@ cmd_genkey (assuan_context_t ctx, char *line) else if (passwd_nonce) newpasswd = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_NONCE); - rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, no_protection, + rc = agent_genkey (ctrl, cache_nonce, opt_timestamp, + (char*)value, valuelen, no_protection, newpasswd, opt_preset, &outbuf); leave: @@ -2176,7 +2197,8 @@ cmd_keywrap_key (assuan_context_t ctx, char *line) static const char hlp_import_key[] = - "IMPORT_KEY [--unattended] [--force] [<cache_nonce>]\n" + "IMPORT_KEY [--unattended] [--force] [--timestamp=<isodate>]\n" + " [<cache_nonce>]\n" "\n" "Import a secret key into the key store. The key is expected to be\n" "encrypted using the current session's key wrapping key (cf. command\n" @@ -2184,13 +2206,16 @@ static const char hlp_import_key[] = "no arguments but uses the inquiry \"KEYDATA\" to ask for the actual\n" "key data. The unwrapped key must be a canonical S-expression. The\n" "option --unattended tries to import the key as-is without any\n" - "re-encryption. Existing key can be overwritten with --force."; + "re-encryption. An existing key can be overwritten with --force.\n" + "If --timestamp is given its value is recorded as the key's creation\n" + "time; the value is expected in ISO format (e.g. \"20030316T120000\")."; static gpg_error_t cmd_import_key (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; int opt_unattended; + time_t opt_timestamp; int force; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; @@ -2204,6 +2229,7 @@ cmd_import_key (assuan_context_t ctx, char *line) gcry_sexp_t openpgp_sexp = NULL; char *cache_nonce = NULL; char *p; + const char *s; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); @@ -2216,6 +2242,22 @@ cmd_import_key (assuan_context_t ctx, char *line) opt_unattended = has_option (line, "--unattended"); force = has_option (line, "--force"); + if ((s=has_option_name (line, "--timestamp"))) + { + if (*s != '=') + { + err = set_error (GPG_ERR_ASS_PARAMETER, "missing value for option"); + goto leave; + } + opt_timestamp = isotime2epoch (s+1); + if (opt_timestamp < 1) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "invalid time value"); + goto leave; + } + } + else + opt_timestamp = 0; line = skip_options (line); for (p=line; *p && *p != ' ' && *p != '\t'; p++) @@ -2287,7 +2329,6 @@ cmd_import_key (assuan_context_t ctx, char *line) goto leave; /* Note that ERR is still set. */ } - if (openpgp_sexp) { /* In most cases the key is encrypted and thus the conversion @@ -2351,10 +2392,12 @@ cmd_import_key (assuan_context_t ctx, char *line) err = agent_protect (key, passphrase, &finalkey, &finalkeylen, ctrl->s2k_count, -1); if (!err) - err = agent_write_private_key (grip, finalkey, finalkeylen, force); + err = agent_write_private_key (grip, finalkey, finalkeylen, force, + opt_timestamp); } else - err = agent_write_private_key (grip, key, realkeylen, force); + err = agent_write_private_key (grip, key, realkeylen, force, + opt_timestamp); leave: gcry_sexp_release (openpgp_sexp); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 06cd1c840..758eab9ab 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1067,7 +1067,7 @@ convert_from_openpgp_native (ctrl_t ctrl, if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen, ctrl->s2k_count, -1)) - agent_write_private_key (grip, protectedkey, protectedkeylen, 1); + agent_write_private_key (grip, protectedkey, protectedkeylen, 1, 0); xfree (protectedkey); } else @@ -1076,7 +1076,7 @@ convert_from_openpgp_native (ctrl_t ctrl, agent_write_private_key (grip, *r_key, gcry_sexp_canon_len (*r_key, 0, NULL,NULL), - 1); + 1, 0); } } diff --git a/agent/findkey.c b/agent/findkey.c index bdb6ab4f5..fa09d645a 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -54,8 +54,8 @@ struct try_unprotect_arg_s /* Note: Ownership of FNAME and FP are moved to this function. */ static gpg_error_t -write_extended_private_key (char *fname, estream_t fp, int update, - const void *buf, size_t len) +write_extended_private_key (char *fname, estream_t fp, int update, int newkey, + const void *buf, size_t len, time_t timestamp) { gpg_error_t err; nvc_t pk = NULL; @@ -93,6 +93,19 @@ write_extended_private_key (char *fname, estream_t fp, int update, if (err) goto leave; + /* If a timestamp has been supplied and the key is new write a + * creation timestamp. (We double check that there is no Created + * item yet.)*/ + if (timestamp && newkey && !nvc_lookup (pk, "Created:")) + { + gnupg_isotime_t timebuf; + + epoch2isotime (timebuf, timestamp); + err = nvc_add (pk, "Created:", timebuf); + if (err) + goto leave; + } + err = es_fseek (fp, 0, SEEK_SET); if (err) goto leave; @@ -136,11 +149,13 @@ write_extended_private_key (char *fname, estream_t fp, int update, } /* 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. */ + * passed as true an existing key with the given GRIP will get + * overwritten. If TIMESTAMP is not zero and the key does not yet + * exists it will be recorded as creation date. */ int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force) + const void *buffer, size_t length, + int force, time_t timestamp) { char *fname; estream_t fp; @@ -208,17 +223,20 @@ agent_write_private_key (const unsigned char *grip, if (first != '(') { /* Key is already in the extended format. */ - return write_extended_private_key (fname, fp, 1, buffer, length); + return write_extended_private_key (fname, fp, 1, 0, buffer, length, + timestamp); } if (first == '(' && opt.enable_extended_key_format) { /* Key is in the old format - but we want the extended format. */ - return write_extended_private_key (fname, fp, 0, buffer, length); + return write_extended_private_key (fname, fp, 0, 0, buffer, length, + timestamp); } } if (opt.enable_extended_key_format) - return write_extended_private_key (fname, fp, 0, buffer, length); + return write_extended_private_key (fname, fp, 0, 1, buffer, length, + timestamp); if (es_fwrite (buffer, length, 1, fp) != 1) { @@ -1596,7 +1614,7 @@ agent_write_shadow_key (const unsigned char *grip, } len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); - err = agent_write_private_key (grip, shdkey, len, force); + err = agent_write_private_key (grip, shdkey, len, force, 0); xfree (shdkey); if (err) log_error ("error writing key: %s\n", gpg_strerror (err)); diff --git a/agent/genkey.c b/agent/genkey.c index cddd67d9a..5c6ae9355 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -33,7 +33,7 @@ static int store_key (gcry_sexp_t private, const char *passphrase, int force, - unsigned long s2k_count) + unsigned long s2k_count, time_t timestamp) { int rc; unsigned char *buf; @@ -68,7 +68,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, buf = p; } - rc = agent_write_private_key (grip, buf, len, force); + rc = agent_write_private_key (grip, buf, len, force, timestamp); xfree (buf); return rc; } @@ -441,9 +441,11 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase using the cache nonce. If NO_PROTECTION is true the key will not be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that - passphrase will be used for the new key. */ + passphrase will be used for the new key. If TIMESTAMP is not zero + it will be recorded as creation date of the key (unless extended + format is disabled) . */ int -agent_genkey (ctrl_t ctrl, const char *cache_nonce, +agent_genkey (ctrl_t ctrl, const char *cache_nonce, time_t timestamp, const char *keyparam, size_t keyparamlen, int no_protection, const char *override_passphrase, int preset, membuf_t *outbuf) { @@ -517,7 +519,7 @@ agent_genkey (ctrl_t ctrl, const char *cache_nonce, /* store the secret key */ if (DBG_CRYPTO) log_debug ("storing private key\n"); - rc = store_key (s_private, passphrase, 0, ctrl->s2k_count); + rc = store_key (s_private, passphrase, 0, ctrl->s2k_count, timestamp); if (!rc) { if (!cache_nonce) @@ -591,7 +593,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, { /* Take an empty string as request not to protect the key. */ err = store_key (s_skey, **passphrase_addr? *passphrase_addr:NULL, 1, - ctrl->s2k_count); + ctrl->s2k_count, 0); } else { @@ -606,7 +608,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey, L_("Please enter the new passphrase"), &pass); if (!err) - err = store_key (s_skey, pass, 1, ctrl->s2k_count); + err = store_key (s_skey, pass, 1, ctrl->s2k_count, 0); if (!err && passphrase_addr) *passphrase_addr = pass; else diff --git a/agent/keyformat.txt b/agent/keyformat.txt index 2e48b346e..c15e4f8d1 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -79,6 +79,42 @@ of a continuation line encodes a newline. Lines containing only whitespace, and lines starting with whitespace followed by '#' are considered to be comments and are ignored. +** Well defined names + +*** Description +This is a human readable string describing the key. + +*** Key +The name "Key" is special in that it is mandatory and must occur only +once. The associated value holds the actual S-expression with the +cryptographic key. The S-expression is formatted using the 'Advanced +Format' (GCRYSEXP_FMT_ADVANCED) that avoids non-printable characters +so that the file can be easily inspected and edited. See section +'Private Key Format' below for details. + +*** Created +The UTC time the key was created in ISO compressed format +(yyyymmddThhmmss). This informarion can be used to re-create an +OpenPGP key. + +*** Label +This is a short human readable description for the key which can be +used by the software to describe the key in a user interface. For +example as part of the description in a prompt for a PIN or +passphrase. It is often used instead of a comment element as present +in the S-expression of the "Key" item. + +*** OpenSSH-cert +This takes a base64 encoded string wrapped so that this +key file can be easily edited with a standard editor. Several of such +items can be used. + +*** Use-for-ssh +If given and the value is "yes" or "1" the key is allowed for use by +gpg-agent's ssh-agent implementation. This is thus the same as +putting the keygrip into the 'sshcontrol' file. Only one such item +should exist. + * Private Key Format ** Unprotected Private Key Format diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 00c0748e1..0ba31c1a0 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -805,12 +805,14 @@ agent_askpin (ctrl_t ctrl, * to stdout. */ int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force) + const void *buffer, size_t length, int force, + time_t timestamp) { char hexgrip[40+4+1]; char *p; (void)force; + (void)timestamp; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); |