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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-08-17 14:21:00 +02:00
parent 6bcb609e1b
commit 0da923a124
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
8 changed files with 109 additions and 38 deletions

View File

@ -442,7 +442,8 @@ gpg_error_t agent_modify_description (const char *in, const char *comment,
const gcry_sexp_t key, char **result); const gcry_sexp_t key, char **result);
int agent_write_private_key (const unsigned char *grip, 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,
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, gpg_error_t agent_key_from_file (ctrl_t ctrl,
const char *cache_nonce, const char *cache_nonce,
const char *desc_text, const char *desc_text,
@ -522,7 +523,7 @@ int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int no_empty,
char **failed_constraint); char **failed_constraint);
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt, gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
char **r_passphrase); 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, const char *keyparam, size_t keyparmlen,
int no_protection, const char *override_passphrase, int no_protection, const char *override_passphrase,
int preset, membuf_t *outbuf); int preset, membuf_t *outbuf);

View File

@ -3036,8 +3036,10 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
if (err) if (err)
goto out; goto out;
/* Store this key to our key storage. */ /* Store this key to our key storage. We do not store a creation
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, NULL, NULL); * timestamp because we simply do not know. */
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0,
NULL, NULL, 0);
if (err) if (err)
goto out; goto out;

View File

@ -928,8 +928,8 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
static const char hlp_genkey[] = static const char hlp_genkey[] =
"GENKEY [--no-protection] [--preset] [--inq-passwd]\n" "GENKEY [--no-protection] [--preset] [--timestamp=<isodate>]\n"
" [--passwd-nonce=<s>] [<cache_nonce>]\n" " [--inq-passwd] [--passwd-nonce=<s>] [<cache_nonce>]\n"
"\n" "\n"
"Generate a new key, store the secret part and return the public\n" "Generate a new key, store the secret part and return the public\n"
"part. Here is an example transaction:\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: D (rsa (n 326487324683264) (e 10001)))\n"
" S: OK key created\n" " S: OK key created\n"
"\n" "\n"
"When the --preset option is used the passphrase for the generated\n" "If 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" "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" "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" "new key. If a --passwd-nonce is used, the corresponding cached\n"
"passphrase is used to protect the new key."; "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 static gpg_error_t
cmd_genkey (assuan_context_t ctx, char *line) cmd_genkey (assuan_context_t ctx, char *line)
{ {
@ -963,6 +965,8 @@ cmd_genkey (assuan_context_t ctx, char *line)
int opt_inq_passwd; int opt_inq_passwd;
size_t n; size_t n;
char *p, *pend; char *p, *pend;
const char *s;
time_t opt_timestamp;
int c; int c;
if (ctrl->restricted) if (ctrl->restricted)
@ -986,6 +990,22 @@ cmd_genkey (assuan_context_t ctx, char *line)
goto leave; 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); line = skip_options (line);
for (p=line; *p && *p != ' ' && *p != '\t'; p++) for (p=line; *p && *p != ' ' && *p != '\t'; p++)
@ -1027,7 +1047,8 @@ cmd_genkey (assuan_context_t ctx, char *line)
else if (passwd_nonce) else if (passwd_nonce)
newpasswd = agent_get_cache (ctrl, passwd_nonce, CACHE_MODE_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); newpasswd, opt_preset, &outbuf);
leave: leave:
@ -2321,7 +2342,8 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
static const char hlp_import_key[] = static const char hlp_import_key[] =
"IMPORT_KEY [--unattended] [--force] [<cache_nonce>]\n" "IMPORT_KEY [--unattended] [--force] [--timestamp=<isodate>]\n"
" [<cache_nonce>]\n"
"\n" "\n"
"Import a secret key into the key store. The key is expected to be\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" "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" "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" "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" "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 static gpg_error_t
cmd_import_key (assuan_context_t ctx, char *line) cmd_import_key (assuan_context_t ctx, char *line)
{ {
ctrl_t ctrl = assuan_get_pointer (ctx); ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err; gpg_error_t err;
int opt_unattended; int opt_unattended;
time_t opt_timestamp;
int force; int force;
unsigned char *wrappedkey = NULL; unsigned char *wrappedkey = NULL;
size_t wrappedkeylen; size_t wrappedkeylen;
@ -2349,6 +2374,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
gcry_sexp_t openpgp_sexp = NULL; gcry_sexp_t openpgp_sexp = NULL;
char *cache_nonce = NULL; char *cache_nonce = NULL;
char *p; char *p;
const char *s;
if (ctrl->restricted) if (ctrl->restricted)
return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); 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"); opt_unattended = has_option (line, "--unattended");
force = has_option (line, "--force"); 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); line = skip_options (line);
for (p=line; *p && *p != ' ' && *p != '\t'; p++) 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. */ goto leave; /* Note that ERR is still set. */
} }
if (openpgp_sexp) if (openpgp_sexp)
{ {
/* In most cases the key is encrypted and thus the conversion /* 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); ctrl->s2k_count, -1);
if (!err) if (!err)
err = agent_write_private_key (grip, finalkey, finalkeylen, force, err = agent_write_private_key (grip, finalkey, finalkeylen, force,
NULL, NULL); NULL, NULL, opt_timestamp);
} }
else 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: leave:
gcry_sexp_release (openpgp_sexp); gcry_sexp_release (openpgp_sexp);

View File

@ -1114,7 +1114,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
&protectedkey, &protectedkeylen, &protectedkey, &protectedkeylen,
ctrl->s2k_count, -1)) ctrl->s2k_count, -1))
agent_write_private_key (grip, protectedkey, protectedkeylen, 1, agent_write_private_key (grip, protectedkey, protectedkeylen, 1,
NULL, NULL); NULL, NULL, 0);
xfree (protectedkey); xfree (protectedkey);
} }
else else
@ -1123,7 +1123,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
agent_write_private_key (grip, agent_write_private_key (grip,
*r_key, *r_key,
gcry_sexp_canon_len (*r_key, 0, NULL,NULL), gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
1, NULL, NULL); 1, NULL, NULL, 0);
} }
} }

View File

@ -82,9 +82,10 @@ linefeed_to_percent0A (const char *string)
/* Note: Ownership of FNAME and FP are moved to this function. */ /* Note: Ownership of FNAME and FP are moved to this function. */
static gpg_error_t 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 void *buf, size_t len,
const char *serialno, const char *keyref) const char *serialno, const char *keyref,
time_t timestamp)
{ {
gpg_error_t err; gpg_error_t err;
nvc_t pk = NULL; 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); err = es_fseek (fp, 0, SEEK_SET);
if (err) 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 /* Write an S-expression formatted key to our key storage. With FORCE
* passed as true an existing key with the given GRIP will get * 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 * overwritten. If SERIALNO and KEYREF are given a Token line is
* the key if the extended format is used. */ * 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 int
agent_write_private_key (const unsigned char *grip, agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force, 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; char *fname;
estream_t fp; estream_t fp;
@ -272,20 +289,20 @@ agent_write_private_key (const unsigned char *grip,
if (first != '(') if (first != '(')
{ {
/* Key is already in the extended format. */ /* 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,
serialno, keyref); serialno, keyref, timestamp);
} }
if (first == '(' && opt.enable_extended_key_format) if (first == '(' && opt.enable_extended_key_format)
{ {
/* Key is in the old format - but we want the extended 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,
serialno, keyref); serialno, keyref, timestamp);
} }
} }
if (opt.enable_extended_key_format) 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,
serialno, keyref); serialno, keyref, timestamp);
if (es_fwrite (buffer, length, 1, fp) != 1) 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); 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); xfree (shdkey);
if (err) if (err)
log_error ("error writing key: %s\n", gpg_strerror (err)); log_error ("error writing key: %s\n", gpg_strerror (err));

View File

@ -32,7 +32,7 @@
static int static int
store_key (gcry_sexp_t private, const char *passphrase, int force, store_key (gcry_sexp_t private, const char *passphrase, int force,
unsigned long s2k_count) unsigned long s2k_count, time_t timestamp)
{ {
int rc; int rc;
unsigned char *buf; unsigned char *buf;
@ -67,7 +67,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
buf = p; 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); xfree (buf);
return rc; 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 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 using the cache nonce. If NO_PROTECTION is true the key will not
be protected by a passphrase. If OVERRIDE_PASSPHRASE is true that 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 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 *keyparam, size_t keyparamlen, int no_protection,
const char *override_passphrase, int preset, membuf_t *outbuf) 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 */ /* store the secret key */
if (DBG_CRYPTO) if (DBG_CRYPTO)
log_debug ("storing private key\n"); 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 (!rc)
{ {
if (!cache_nonce) 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. */ /* Take an empty string as request not to protect the key. */
err = store_key (s_skey, **passphrase_addr? *passphrase_addr:NULL, 1, err = store_key (s_skey, **passphrase_addr? *passphrase_addr:NULL, 1,
ctrl->s2k_count); ctrl->s2k_count, 0);
} }
else else
{ {
@ -588,7 +590,7 @@ agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
L_("Please enter the new passphrase"), L_("Please enter the new passphrase"),
&pass); &pass);
if (!err) 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) if (!err && passphrase_addr)
*passphrase_addr = pass; *passphrase_addr = pass;
else else

View File

@ -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 so that the file can be easily inspected and edited. See section
'Private Key Format' below for details. '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 *** Label
This is a short human readable description for the key which can be 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 used by the software to describe the key in a user interface. For

View File

@ -816,7 +816,8 @@ agent_askpin (ctrl_t ctrl,
int int
agent_write_private_key (const unsigned char *grip, agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force, 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 hexgrip[40+4+1];
char *p; char *p;
@ -824,6 +825,7 @@ agent_write_private_key (const unsigned char *grip,
(void)force; (void)force;
(void)serialno; (void)serialno;
(void)keyref; (void)keyref;
(void)timestamp;
bin2hex (grip, 20, hexgrip); bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key"); strcpy (hexgrip+40, ".key");