From 0da923a1240ac78d60c92cdd8488c4e405c3243b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 17 Aug 2020 14:21:00 +0200 Subject: [PATCH] agent: Allow to pass a timestamp to genkey and import. * agent/command.c (cmd_genkey): Add option --timestamp. (cmd_import_key): Ditto. * agent/genkey.c (store_key): Add arg timestamp and change callers. (agent_genkey): Ditto. * agent/findkey.c (write_extended_private_key): Add args timestamp and new key to write a Created line. (agent_write_private_key): Add arg timestamp. (agent_write_shadow_key): Ditto. agent/protect-tool.c (agent_write_private_key): Ditto as dummy arg. Signed-off-by: Werner Koch --- agent/agent.h | 5 ++-- agent/command-ssh.c | 6 ++-- agent/command.c | 66 ++++++++++++++++++++++++++++++++++++-------- agent/cvt-openpgp.c | 4 +-- agent/findkey.c | 41 +++++++++++++++++++-------- agent/genkey.c | 16 ++++++----- agent/keyformat.txt | 5 ++++ agent/protect-tool.c | 4 ++- 8 files changed, 109 insertions(+), 38 deletions(-) diff --git a/agent/agent.h b/agent/agent.h index 7f18eb601..25d9e2d29 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -442,7 +442,8 @@ 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 char *serialno, const char *keyref); + const char *serialno, const char *keyref, + time_t timestamp); gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, @@ -522,7 +523,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 664bd0d30..9081f6f89 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -3036,8 +3036,10 @@ 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, NULL, NULL); + /* 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, + NULL, NULL, 0); if (err) goto out; diff --git a/agent/command.c b/agent/command.c index 6efde5e28..b2bb72ace 100644 --- a/agent/command.c +++ b/agent/command.c @@ -928,8 +928,8 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line) static const char hlp_genkey[] = - "GENKEY [--no-protection] [--preset] [--inq-passwd]\n" - " [--passwd-nonce=] []\n" + "GENKEY [--no-protection] [--preset] [--timestamp=]\n" + " [--inq-passwd] [--passwd-nonce=] []\n" "\n" "Generate a new key, store the secret part and return the public\n" "part. Here is an example transaction:\n" @@ -942,11 +942,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) { @@ -963,6 +965,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) @@ -986,6 +990,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++) @@ -1027,7 +1047,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: @@ -2321,7 +2342,8 @@ cmd_keywrap_key (assuan_context_t ctx, char *line) static const char hlp_import_key[] = - "IMPORT_KEY [--unattended] [--force] []\n" + "IMPORT_KEY [--unattended] [--force] [--timestamp=]\n" + " []\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" @@ -2329,13 +2351,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; @@ -2349,6 +2374,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)); @@ -2361,6 +2387,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++) @@ -2434,7 +2476,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 @@ -2499,10 +2540,11 @@ cmd_import_key (assuan_context_t ctx, char *line) ctrl->s2k_count, -1); if (!err) err = agent_write_private_key (grip, finalkey, finalkeylen, force, - NULL, NULL); + NULL, NULL, opt_timestamp); } else - err = agent_write_private_key (grip, key, realkeylen, force, NULL, NULL); + err = agent_write_private_key (grip, key, realkeylen, force, NULL, NULL, + opt_timestamp); leave: gcry_sexp_release (openpgp_sexp); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 5d60b66f3..c43809f90 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1114,7 +1114,7 @@ convert_from_openpgp_native (ctrl_t ctrl, &protectedkey, &protectedkeylen, ctrl->s2k_count, -1)) agent_write_private_key (grip, protectedkey, protectedkeylen, 1, - NULL, NULL); + NULL, NULL, 0); xfree (protectedkey); } else @@ -1123,7 +1123,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, NULL, NULL); + 1, NULL, NULL, 0); } } diff --git a/agent/findkey.c b/agent/findkey.c index 0951a754e..a9a8fb851 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -82,9 +82,10 @@ linefeed_to_percent0A (const char *string) /* 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, +write_extended_private_key (char *fname, estream_t fp, int update, int newkey, const void *buf, size_t len, - const char *serialno, const char *keyref) + const char *serialno, const char *keyref, + time_t timestamp) { gpg_error_t err; nvc_t pk = NULL; @@ -153,6 +154,19 @@ write_extended_private_key (char *fname, estream_t fp, int update, } } + /* If a timestamp has been supplied and the key is new write a + * creation timestamp. (We douple 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) @@ -199,12 +213,15 @@ 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. If SERIALNO and KEYREF are given a Token line is added to - * the key if the extended format is used. */ + * overwritten. If SERIALNO and KEYREF are given a Token line is + * added to the key if the extended format is used. If TIMESTAMP is + * not zero and the key doies 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 char *serialno, const char *keyref) + const char *serialno, const char *keyref, + time_t timestamp) { char *fname; estream_t fp; @@ -272,20 +289,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, - serialno, keyref); + return write_extended_private_key (fname, fp, 1, 0, buffer, length, + serialno, keyref, 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, - serialno, keyref); + return write_extended_private_key (fname, fp, 0, 0, buffer, length, + serialno, keyref, timestamp); } } if (opt.enable_extended_key_format) - return write_extended_private_key (fname, fp, 0, buffer, length, - serialno, keyref); + return write_extended_private_key (fname, fp, 0, 1, buffer, length, + serialno, keyref, timestamp); if (es_fwrite (buffer, length, 1, fp) != 1) { @@ -1552,7 +1569,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, serialno, keyid); + err = agent_write_private_key (grip, shdkey, len, force, serialno, keyid, 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 970876ba1..9b47f0fac 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -32,7 +32,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; @@ -67,7 +67,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, buf = p; } - rc = agent_write_private_key (grip, buf, len, force, NULL, NULL); + rc = agent_write_private_key (grip, buf, len, force, NULL, NULL, timestamp); xfree (buf); return rc; } @@ -423,9 +423,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) { @@ -499,7 +501,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) @@ -573,7 +575,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 { @@ -588,7 +590,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 e2ca05c84..7e1b9613d 100644 --- a/agent/keyformat.txt +++ b/agent/keyformat.txt @@ -86,6 +86,11 @@ 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 diff --git a/agent/protect-tool.c b/agent/protect-tool.c index a95f418e6..8325f2564 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -816,7 +816,8 @@ agent_askpin (ctrl_t ctrl, int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force, - const char *serialno, const char *keyref) + const char *serialno, const char *keyref, + time_t timestamp) { char hexgrip[40+4+1]; char *p; @@ -824,6 +825,7 @@ agent_write_private_key (const unsigned char *grip, (void)force; (void)serialno; (void)keyref; + (void)timestamp; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key");