agent: Put Token lines into the key files.
authorWerner Koch <wk@gnupg.org>
Fri, 3 May 2019 13:54:54 +0000 (15:54 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 3 May 2019 13:54:54 +0000 (15:54 +0200)
* 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>
agent/agent.h
agent/command-ssh.c
agent/command.c
agent/cvt-openpgp.c
agent/findkey.c
agent/genkey.c
agent/keyformat.txt
agent/protect-tool.c
agent/protect.c

index 0f804cd..b7eacf4 100644 (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,
index ebd28ab..727bb9b 100644 (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;
 
index 082ed41..9bb6fa3 100644 (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);
index 06cd1c8..42052d4 100644 (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);
         }
     }
 
index 89a18fa..157870e 100644 (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));
index d5c80d0..84342f9 100644 (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;
 }
index c7426db..058fb01 100644 (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
 
index ec7b476..30d78cd 100644 (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");
index 61fb8f4..c7fb773 100644 (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)