agent: Put Token lines into the key files.

* agent/findkey.c (write_extended_private_key): Add args serialno and
keyref.  Write a Token line if that does not yet exist.
(agent_write_private_key): Add args serialno and keyref and change all
callers.
(agent_write_shadow_key): Skip leading spaces.
* agent/keyformat.txt: Improve extended key format docs.
--

Noet that the extended key forma is the defaqult in 2.3.  This patch
is a first step to better handle tokens which carray the same key.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-05-03 15:54:54 +02:00
parent c9fa28bfad
commit bdf252e76a
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
9 changed files with 121 additions and 32 deletions

View File

@ -414,7 +414,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,
const char *serialno, const char *keyref);
gpg_error_t agent_key_from_file (ctrl_t ctrl,
const char *cache_nonce,
const char *desc_text,

View File

@ -3142,7 +3142,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
goto out;
/* Store this key to our key storage. */
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0);
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, NULL, NULL);
if (err)
goto out;

View File

@ -2255,10 +2255,11 @@ 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,
NULL, NULL);
}
else
err = agent_write_private_key (grip, key, realkeylen, force);
err = agent_write_private_key (grip, key, realkeylen, force, NULL, NULL);
leave:
gcry_sexp_release (openpgp_sexp);

View File

@ -1067,7 +1067,8 @@ 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,
NULL, NULL);
xfree (protectedkey);
}
else
@ -1076,7 +1077,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, NULL, NULL);
}
}

View File

@ -55,12 +55,14 @@ 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)
const void *buf, size_t len,
const char *serialno, const char *keyref)
{
gpg_error_t err;
nvc_t pk = NULL;
gcry_sexp_t key = NULL;
int remove = 0;
char *token = NULL;
if (update)
{
@ -93,6 +95,37 @@ write_extended_private_key (char *fname, estream_t fp, int update,
if (err)
goto leave;
/* If requested write a Token line. */
if (serialno && keyref)
{
nve_t item;
const char *s;
token = strconcat (serialno, " ", keyref, NULL);
if (!token)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* fixme: the strcmp should compare only the first two strings. */
for (item = nvc_lookup (pk, "Token:");
item;
item = nve_next_value (item, "Token:"))
if ((s = nve_value (item)) && !strcmp (s, token))
break;
if (!item)
{
/* No token or no token with that value exists. Add a new
* one so that keys which have been stored on several cards
* are well supported. */
err = nvc_add (pk, "Token:", token);
if (err)
goto leave;
}
}
err = es_fseek (fp, 0, SEEK_SET);
if (err)
goto leave;
@ -132,15 +165,18 @@ write_extended_private_key (char *fname, estream_t fp, int update,
xfree (fname);
gcry_sexp_release (key);
nvc_release (pk);
xfree (token);
return err;
}
/* 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 SERIALNO and KEYREF are give an a Token line is added to
* th key if the extended format ist used. */
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)
{
char *fname;
estream_t fp;
@ -208,17 +244,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, buffer, length,
serialno, keyref);
}
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, buffer, length,
serialno, keyref);
}
}
if (opt.enable_extended_key_format)
return write_extended_private_key (fname, fp, 0, buffer, length);
return write_extended_private_key (fname, fp, 0, buffer, length,
serialno, keyref);
if (es_fwrite (buffer, length, 1, fp) != 1)
{
@ -1580,6 +1619,13 @@ agent_write_shadow_key (const unsigned char *grip,
unsigned char *shdkey;
size_t len;
/* Just in case some caller did not parse the stuff correctly, skip
* leading spaces. */
while (spacep (serialno))
serialno++;
while (spacep (keyid))
keyid++;
shadow_info = make_shadow_info (serialno, keyid);
if (!shadow_info)
return gpg_error_from_syserror ();
@ -1593,7 +1639,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, serialno, keyid);
xfree (shdkey);
if (err)
log_error ("error writing key: %s\n", gpg_strerror (err));

View File

@ -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, NULL, NULL);
xfree (buf);
return rc;
}

View File

@ -18,7 +18,8 @@ hexadecimal representation of the keygrip[2] and suffixed with ".key".
* Extended Private Key Format
GnuPG 2.3+ will use a new format to store private keys that is both
** Overview
GnuPG 2.3+ uses a new format to store private keys that is both
more flexible and easier to read and edit by human beings. The new
format stores name,value-pairs using the common mail and http header
convention. Example (here indented with two spaces):
@ -28,6 +29,8 @@ convention. Example (here indented with two spaces):
Use-for-ssh: yes
OpenSSH-cert: long base64 encoded string wrapped so that this
key file can be easily edited with a standard editor.
Token: D2760001240102000005000011730000 OPENPGP.1
Token: FF020001008A77C1 PIV.9C
Key: (shadowed-private-key
(rsa
(n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900
@ -52,33 +55,66 @@ Keys in the extended format can be recognized by looking at the first
byte of the file. If it starts with a '(' it is a naked S-expression,
otherwise it is a key in extended format.
** Names
*** Names
A name must start with a letter and end with a colon. Valid
characters are all ASCII letters, numbers and the hyphen. Comparison
of names is done case insensitively. Names may be used several times
to represent an array of values.
The name "Key:" is special in that it may occur only once and 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.
** Values
to represent an array of values. Note that the name "Key" is special
in that it is madandory must occur only once.
*** Values
Values are UTF-8 encoded strings. Values can be wrapped at any point,
and continued in the next line indicated by leading whitespace. A
continuation line with one leading space does not introduce a blank so
that the lines can be effectively concatenated. A blank line as part
of a continuation line encodes a newline.
** Comments
*** Comments
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.
*** 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 preent 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.
*** Token
If such an item exists it overrides the info given by the "shadow"
parameter in the S-expression. Using this item makes it possible to
describe a key which is stored on several tokens and also makes it
easy to update this info using a standard editor. The syntax is the
same as with the "shadow" parameter:
- Serialnumber of the token
- Key reference from the token in full format (e.g. "OpenPGP.2")
- An optional fixed length of the PIN.
*** 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

View File

@ -799,12 +799,15 @@ 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,
const char *serialno, const char *keyref)
{
char hexgrip[40+4+1];
char *p;
(void)force;
(void)serialno;
(void)keyref;
bin2hex (grip, 20, hexgrip);
strcpy (hexgrip+40, ".key");

View File

@ -1667,7 +1667,8 @@ agent_get_shadow_info (const unsigned char *shadowkey,
R_HEXSN and the Id string as a malloced string at R_IDSTR. On
error an error code is returned and NULL is stored at the result
parameters addresses. If the serial number or the ID string is not
required, NULL may be passed for them. */
required, NULL may be passed for them. Note that R_PINLEN is
currently not used by any caller. */
gpg_error_t
parse_shadow_info (const unsigned char *shadow_info,
char **r_hexsn, char **r_idstr, int *r_pinlen)