[PATCH] Re: command line keytocard

Meno Abels meno.abels at adviser.com
Fri Dec 2 23:59:54 CET 2016


Now —quick-keytocard is working for me. I can pass the passphrase
and the adminpin down. I solved it by enabling the multiple use of
—passphrase-fd/file

here the patch for the passphrase change

commit 58bbd77b695a4beab60d6033024e3a7045dd078e
gpg: Signature made Fri Dec  2 23:55:25 2016 CET
gpg:                using RSA key F78D5B547A9BB0E8A174C0F5060FF53CB3A32992
gpg: Good signature from "Meno Abels <meno.abels at adviser.com>" [ultimate]
Author: Meno Abels <meno.abels at adviser.com>
Date:   Fri Dec 2 23:33:19 2016

    gpg: Enable to pass multiple passphrases
    
    This feature is required to copy keys in a smartcard without
    any user interaction.
    It currently only used by --quick-keytocard with a command line
    like this:
    gpg --batch --pinentry-mode loopback --passphrase-file ~/p1
    --passphrase-file ~/p2 --quick-keytocard <keyid> auth
    
    This is not a designed solution(hack). This could break any
    time.
    Next I will try to build tests explain the odd behavior.
    The problem is (get_static_passphrase) now have a side
    effect that after the call to (get_static_passphrase) the
    next password will be returned on the next call to
    (get_static_passphrase). And (get_static_passphrase) the
    order and of calls defines the returned password.
    
    I discovered that in gpgsm almost the same code is used.
    But it is in different files. So should id refactor this?
    Currently i did not changed this gpgsm.
    
    * g10/gpg.c changed switch cases oPassphraseFD/oPassphraseFile
    to store the incoming file descriptor.
    * g10/gpg.c passed the collected fds to the new
    (read_passphrase_from_fds)
    * g10/keydb.h added STATIC_PASSWORD_COUNT constant (4)
    * g10/keydb.h modified (read_passphrase_from_fd) to
    (read_passphrase_from_fds) to pass multiple fds
    * g10/passphrase.c removed fd_passwd
    * g10/passphrase.c added fd_passwds[STATIC_PASSWORD_COUNT]
    * g10/passphrase.c added fd_passwd_idx use count
    * g10/passphrase.c added fd_passwd_req roundrobin counter
    * g10/passphrase.c (have_static_passphrase) uses fd_passwd_idx
    * g10/passphrase.c (get_static_passphrase) roundrobin behavior
    * g10/passphrase.c (set_passphrase_from_string) appends to fd_passwds
    * g10/passphrase.c (read_passphrase_from_fd) static and wipe added
    * g10/passphrase.c (read_passphrase_from_fds) loops over fds
    * g10/passphrase.c (passphrase_to_dek) change direct use of
    fw_passwd to (get_static_passphrase)
    
    Signed-off-by: Meno Abels <meno.abels at adviser.com>

diff --git a/doc/gpg.texi b/doc/gpg.texi
index 7adccf3..78da66a 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -678,8 +678,8 @@ is written. If 'auth' is choosen the @code{slot} 3 is used.
 To write to a keyslot needs the smartcard admin pin. This is usally
 requested with pinentry. In the case of passing the password through
 loopback pinentry you have to provide a second password. This could
-be done with --passphrase-fds. The second specified fd should provide
-the admin pin.
+be done with --passphrase-fd. You can call --passphrase-fd/file
+multiple to pass the admin pin.
 
 @item --gen-key
 @opindex gen-key
@@ -2965,8 +2965,8 @@ passphrase.  Defaults to 1 repetition.
 @opindex passphrase-fd
 Read the passphrase from file descriptor @code{n}. Only the first line
 will be read from file descriptor @code{n}. If you use 0 for @code{n},
-the passphrase will be read from STDIN. This can only be used if only
-one passphrase is supplied.
+the passphrase will be read from STDIN. This can called multiple to
+pass multiple passphrases.
 
 Note that this passphrase is only used if the option @option{--batch}
 has also been given.  This is different from GnuPG version 1.x.
@@ -2976,7 +2976,8 @@ has also been given.  This is different from GnuPG version 1.x.
 Read the passphrase from file @code{file}. Only the first line will
 be read from file @code{file}. This can only be used if only one
 passphrase is supplied. Obviously, a passphrase stored in a file is
-of questionable security if other users can read this file. Don't use
+of questionable security if other users can read this file.
+This can called multiple to pass multiple passphrases.  Don't use
 this option if you can avoid it.
 Note that this passphrase is only used if the option @option{--batch}
 has also been given.  This is different from GnuPG version 1.x.
@@ -2985,8 +2986,9 @@ has also been given.  This is different from GnuPG version 1.x.
 @opindex passphrase
 Use @code{string} as the passphrase. This can only be used if only one
 passphrase is supplied. Obviously, this is of very questionable
-security on a multi-user system. Don't use this option if you can
-avoid it.
+security on a multi-user system. This can called multiple to
+pass multiple passphrases.
+Don't use this option if you can avoid it.
 Note that this passphrase is only used if the option @option{--batch}
 has also been given.  This is different from GnuPG version 1.x.
 
diff --git a/g10/gpg.c b/g10/gpg.c
index deef69a..edca6c4 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -2270,7 +2270,8 @@ main (int argc, char **argv)
     char *pers_compress_list = NULL;
     int eyes_only=0;
     int multifile=0;
-    int pwfd = -1;
+    int pwfds_cnt = 0;
+    int pwfds[STATIC_PASSWORD_COUNT];
     int ovrseskeyfd = -1;
     int fpr_maybe_cmd = 0; /* --fingerprint maybe a command.  */
     int any_explicit_recipient = 0;
@@ -3036,10 +3037,22 @@ main (int argc, char **argv)
 	    set_passphrase_from_string(pargs.r.ret_str);
 	    break;
 	  case oPassphraseFD:
-            pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
+        if (pwfds_cnt < sizeof(pwfds)/sizeof(pwfds[0]))
+        {
+            pwfds[pwfds_cnt++] = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
+        }
+        else
+        {
+            log_error (_("to much passphrase-fds\n"));
+        }
             break;
 	  case oPassphraseFile:
-            pwfd = open_info_file (pargs.r.ret_str, 0, 1);
+        if (pwfds_cnt < sizeof(pwfds)/sizeof(pwfds[0]))
+        {
+            pwfds[pwfds_cnt++] = open_info_file (pargs.r.ret_str, 0, 1);
+        } else {
+            log_error (_("to much passphrase-fds\n"));
+        }
             break;
 	  case oPassphraseRepeat:
             opt.passphrase_repeat = pargs.r.ret_int;
@@ -3876,8 +3889,8 @@ main (int argc, char **argv)
       g10_exit(0);
 
 
-    if (pwfd != -1)  /* Read the passphrase now. */
-      read_passphrase_from_fd (pwfd);
+    /* Read the passphrase now. */
+    read_passphrase_from_fds (pwfds, pwfds_cnt);
 
     if (ovrseskeyfd != -1 )  /* Read the sessionkey now. */
       read_sessionkey_from_fd (ovrseskeyfd);
diff --git a/g10/keydb.h b/g10/keydb.h
index 8daa9ee..22c2170 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -26,6 +26,7 @@
 #include "util.h"
 #include "packet.h"
 
+#define STATIC_PASSWORD_COUNT 4
 /* What qualifies as a certification (rather than a signature?) */
 #define IS_CERT(s)       (IS_KEY_SIG(s) || IS_UID_SIG(s) || IS_SUBKEY_SIG(s) \
                          || IS_KEY_REV(s) || IS_UID_REV(s) || IS_SUBKEY_REV(s))
@@ -251,7 +252,7 @@ unsigned char encode_s2k_iterations (int iterations);
 int  have_static_passphrase(void);
 const char *get_static_passphrase (void);
 void set_passphrase_from_string(const char *pass);
-void read_passphrase_from_fd( int fd );
+void read_passphrase_from_fds( int *fds, int cnt);
 void passphrase_clear_cache (const char *cacheid);
 DEK *passphrase_to_dek_ext(u32 *keyid, int pubkey_algo,
                            int cipher_algo, STRING2KEY *s2k, int mode,
diff --git a/g10/passphrase.c b/g10/passphrase.c
index ccd232a..4067af4 100644
--- a/g10/passphrase.c
+++ b/g10/passphrase.c
@@ -43,7 +43,9 @@
 #include "call-agent.h"
 #include "../common/shareddefs.h"
 
-static char *fd_passwd = NULL;
+static char *fd_passwds[STATIC_PASSWORD_COUNT];
+static int   fd_passwd_idx = 0;
+static int   fd_passwd_req = 0;
 static char *next_pw = NULL;
 static char *last_pw = NULL;
 
@@ -102,7 +104,7 @@ encode_s2k_iterations (int iterations)
 int
 have_static_passphrase()
 {
-  return (!!fd_passwd
+  return (fd_passwd_idx > 0
           && (opt.batch || opt.pinentry_mode == PINENTRY_MODE_LOOPBACK));
 }
 
@@ -111,9 +113,14 @@ have_static_passphrase()
    be returned if no passphrase has been set; better use
    have_static_passphrase first.  */
 const char *
-get_static_passphrase (void)
+get_static_passphrase ()
 {
-  return fd_passwd;
+  int idx = fd_passwd_req++ % fd_passwd_idx;
+  if (fd_passwd_req > fd_passwd_idx)
+  {
+    log_info(_("get_static_passphrase did a wrap a round %d"), fd_passwd_req);
+  }
+  return fd_passwds[idx];
 }
 
 
@@ -154,14 +161,19 @@ get_last_passphrase()
 void
 set_passphrase_from_string(const char *pass)
 {
-  xfree (fd_passwd);
-  fd_passwd = xmalloc_secure(strlen(pass)+1);
-  strcpy (fd_passwd, pass);
+  if (fd_passwd_idx < sizeof(fd_passwds)/sizeof(fd_passwds[0]))
+  {
+    // xfree (fd_passwd);
+    fd_passwds[fd_passwd_idx] = xmalloc_secure(strlen(pass)+1);
+    strcpy (fd_passwds[fd_passwd_idx], pass);
+    ++fd_passwd_idx;
+  } else {
+    log_info(_("try to set too much passwords"));
+  }
 }
 
-
-void
-read_passphrase_from_fd( int fd )
+static void
+read_passphrase_from_fd( int fd)
 {
   int i, len;
   char *pw;
@@ -200,8 +212,22 @@ read_passphrase_from_fd( int fd )
   if (!opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
     tty_printf("\b\b\b   \n" );
 
-  xfree ( fd_passwd );
-  fd_passwd = pw;
+  set_passphrase_from_string(pw);
+  // wipe the pass password
+  for (i = strlen(pw)-1; i >= 0; --i) {
+    pw[i] = 'X';
+  }
+}
+
+
+void
+read_passphrase_from_fds( int *fds, int cnt )
+{
+  int i;
+  for(i = 0; i < cnt; ++i)
+  {
+      read_passphrase_from_fd(fds[i]);
+  }
 }
 
 
@@ -360,9 +386,10 @@ passphrase_to_dek (int cipher_algo, STRING2KEY *s2k,
     }
   else if ( have_static_passphrase () )
     {
+      const char *s = get_static_passphrase();
       /* Return the passphrase we have stored in FD_PASSWD. */
-      pw = xmalloc_secure ( strlen(fd_passwd)+1 );
-      strcpy ( pw, fd_passwd );
+      pw = xmalloc_secure ( strlen(s)+1 );
+      strcpy ( pw, s );
     }
   else
     {


> On 2 Dec 2016, at 21:50, Meno Abels <meno.abels at adviser.com> wrote:
> 
> Hi,
> 
> attachments does not work: 
> 
> From bf9027719a27dc6c0d5eba5d8a067542ff79514d Mon Sep 17 00:00:00 2001
> From: Meno Abels <meno.abels at adviser.com>
> Date: Fri, 2 Dec 2016 21:23:39 +0100
> Subject: [PATCH] gpg: New option --quick-keytocard
> 
> * wrote the documentation for the new option (untested)
> * g10/card-util.c (send_keytocard) added
> * g10/gpg.c added aQuickKeyToCard to cmd_and_opt_values
> * g10/gpg.c added ARGPARSE_c with aQuickKeyToCard
> * g10/gpg.c added handling for aQuickKeyToCard
> * g10/keyedit.c (get_keyno_from_slot_usage) new function
> maps commandline option to keyno.
> * g10/keyedit.c (is_keyno_matching_usage) tests if the
> key has the right usage for the keyno.
> * g10/keyedit.c (keyedit_quick_keytocard) new function
> to prepare the commandline parameter to call
> (send_keytocard) to transfer the key.
> * g10/main.h (keyedit_quick_keytocard) forward decl
> * g10/main.h (send_keytocard) forward decl
> 
> Tested: by hand i send ~15 keys to reseted yubikeys
> and try successfully to use the key for sign,encr,auth
> Missing: in batch we need to pass the adminpin via
> loopback pinentry
> 
> Signed-off-by: Meno Abels <meno.abels at adviser.com>
> ---
> doc/gpg.texi    | 18 +++++++++++
> g10/card-util.c | 62 ++++++++++++++++++++++++++++++++++++
> g10/gpg.c       | 25 +++++++++++++++
> g10/keyedit.c   | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> g10/main.h      |  4 +++
> 5 files changed, 208 insertions(+)
> 
> diff --git a/doc/gpg.texi b/doc/gpg.texi
> index b01d0a3..7adccf3 100644
> --- a/doc/gpg.texi
> +++ b/doc/gpg.texi
> @@ -663,6 +663,24 @@ for the subkey.  Several formats are supported; commonly the ISO
> YYYY-MM-DD format is used.  The values ``never'', ``none'', or ``-''
> can be used for no expiration date.
> 
> +
> + at item --quick-keytocard @code{fpr} (@code{slot}|@code{usage}) [@code{serial}]
> + at opindex quick-keytocard
> +Sends the key identified by the fingerprint @code{fpr} to the by
> + at code{serial} identified smartcard. If @code{serial} is not set
> +the first card will be used. The @code{serial} is the serialnumber
> +of the smartcard. This number is found in "--card-status".
> +The @code{slot} and @code{usage} specify the slot in the smartcard
> +which should receive the choosen key. @code{slot} is just the number
> +of the slot starting with 1. The @code{usage} maps if 'sign' is to
> + at code{slot} 1. If @code{usage} is set to 'encr' the @code{slot} 2
> +is written. If 'auth' is choosen the @code{slot} 3 is used.
> +To write to a keyslot needs the smartcard admin pin. This is usally
> +requested with pinentry. In the case of passing the password through
> +loopback pinentry you have to provide a second password. This could
> +be done with --passphrase-fds. The second specified fd should provide
> +the admin pin.
> +
> @item --gen-key
> @opindex gen-key
> Generate a new key pair using the current default parameters.  This is
> diff --git a/g10/card-util.c b/g10/card-util.c
> index e358572..b905a29 100644
> --- a/g10/card-util.c
> +++ b/g10/card-util.c
> @@ -1628,6 +1628,68 @@ card_store_subkey (KBNODE node, int use)
>   return okay;
> }
> 
> +int
> +send_keytocard(PKT_public_key *pk, int keyno, const char *serialno) {
> +  gnupg_isotime_t timebuf;
> +  struct agent_card_info_s info;
> +  unsigned int nbits;
> +  gpg_error_t err = 0;
> +  int rc = 0;
> +  char *hexgrip = 0;
> +  int okay = 0;
> +
> +  if (get_info_for_key_operation (&info))
> +  {
> +    log_error (_("get_info_for_key_operations: failed:%s"),
> +               gpg_strerror (err));
> +    goto leave;
> +  }
> +  if (serialno && *serialno && strcmp(info.serialno, serialno)) {
> +    log_error (_("The card serial no does not match %s!=%s"), serialno,
> +               info.serialno);
> +    goto leave;
> +  }
> +  if (!info.extcap.ki)
> +  {
> +    log_error (_("The card does not support the import of keys"));
> +    goto leave;
> +  }
> +  nbits = nbits_from_pk (pk);
> +  if (!info.is_v2 && nbits != 1024)
> +  {
> +    log_error (_("You may only store a 1024 bit RSA key on the card"));
> +    goto leave;
> +  }
> +  if (info.is_v2 && !info.extcap.aac
> +    && info.key_attr[keyno-1].nbits != nbits)
> +  {
> +    log_error (_("Key does not match the card's capability. %d %d"), keyno, nbits);
> +    goto leave;
> +  }
> +  if ((keyno == 1 && info.fpr1valid)
> +      || (keyno == 2 && info.fpr2valid)
> +      || (keyno == 3 && info.fpr3valid)) {
> +    log_info (_("replace existing key in slot %d"), keyno);
> +  }
> +  if ((err = hexkeygrip_from_pk (pk, &hexgrip))) {
> +    log_error (_("hexkeygrip_from_pk failed with %s"), gpg_strerror(err));
> +    goto leave;
> +  }
> +  epoch2isotime (timebuf, (time_t)pk->timestamp);
> +  if ((err = agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf))) {
> +    log_error (_("agent_keytocard failed: %s\n"), gpg_strerror (rc));
> +    if ((rc = agent_scd_learn (NULL, 1))) {
> +      log_error (_("agent_scd_learn failed: %s\n"), gpg_strerror (rc));
> +    }
> +  } else {
> +    okay = 1;
> +  }
> +leave:
> +  xfree (hexgrip);
> +  agent_release_card_info (&info);
> +  return okay;
> +}
> +
> 
> 
> /* Direct sending of an hex encoded APDU with error printing.  */
> diff --git a/g10/gpg.c b/g10/gpg.c
> index 7cf51f2..deef69a 100644
> --- a/g10/gpg.c
> +++ b/g10/gpg.c
> @@ -123,6 +123,7 @@ enum cmd_and_opt_values
>     aQuickAddUid,
>     aQuickAddKey,
>     aQuickRevUid,
> +    aQuickKeyToCard,
>     aListConfig,
>     aListGcryptConfig,
>     aGPGConfList,
> @@ -448,6 +449,8 @@ static ARGPARSE_OPTS opts[] = {
>   ARGPARSE_c (aQuickAddKey,  "quick-addkey", "@"),
>   ARGPARSE_c (aQuickRevUid,  "quick-revuid",
>               N_("quickly revoke a user-id")),
> +  ARGPARSE_c (aQuickKeyToCard,  "quick-keytocard",
> +              N_("send a secret key to a smartcard")),
>   ARGPARSE_c (aFullKeygen,  "full-gen-key" ,
>               N_("full featured key pair generation")),
>   ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
> @@ -2549,6 +2552,7 @@ main (int argc, char **argv)
> 	  case aQuickAddUid:
> 	  case aQuickAddKey:
> 	  case aQuickRevUid:
> +	  case aQuickKeyToCard:
> 	  case aExportOwnerTrust:
> 	  case aImportOwnerTrust:
>           case aRebuildKeydbCaches:
> @@ -3953,6 +3957,7 @@ main (int argc, char **argv)
>       case aQuickAddUid:
>       case aQuickAddKey:
>       case aQuickRevUid:
> +      case aQuickKeyToCard:
>       case aFullKeygen:
>       case aKeygen:
>       case aImport:
> @@ -4383,6 +4388,26 @@ main (int argc, char **argv)
>           keyedit_quick_revuid (ctrl, uid, uidtorev);
>         }
> 	break;
> +      case aQuickKeyToCard:
> +        {
> +          const char *x_fpr, *x_slot_usage, *x_smartcard_serial;
> +
> +          if (argc < 1 || argc > 4)
> +            wrong_args ("--quick-addkey FINGERPRINT (SLOT|USAGE) [SMARTCARDSERIAL]");
> +          x_fpr = *argv++; argc--;
> +          x_slot_usage = "";
> +          x_smartcard_serial = "";
> +          if (argc)
> +            {
> +              x_slot_usage = *argv++; argc--;
> +              if (argc)
> +                {
> +                  x_smartcard_serial = *argv++; argc--;
> +                }
> +            }
> +          keyedit_quick_keytocard (ctrl, x_fpr, x_slot_usage, x_smartcard_serial);
> +        }
> +	break;
> 
>       case aFastImport:
>         opt.import_options |= IMPORT_FAST;
> diff --git a/g10/keyedit.c b/g10/keyedit.c
> index 94fa8c4..764c0df 100644
> --- a/g10/keyedit.c
> +++ b/g10/keyedit.c
> @@ -3341,6 +3341,105 @@ keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
>   keydb_release (kdbhd);
> }
> 
> +int
> +get_keyno_from_slot_usage(const char *slot_usage)
> +{
> +  int keyno = 1;
> +  if (strcmp(slot_usage, "sign"))
> +  {
> +    keyno = 2;
> +    if (strcmp(slot_usage, "encr"))
> +    {
> +      keyno = 3;
> +      if (strcmp(slot_usage, "auth"))
> +      {
> +        keyno = atoi(slot_usage);
> +      }
> +    }
> +  }
> +  return keyno;
> +}
> +
> +int
> +is_keyno_matching_usage(int keyno, int usage)
> +{
> +  if (keyno == 1) {
> +    if (!(usage & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT))) {
> +      log_error (_("the key for slot 1 is not for signature or certification"));
> +      return 0;
> +    }
> +  }
> +  if (keyno == 2) {
> +    if (!(usage & (PUBKEY_USAGE_ENC))) {
> +      log_error (_("the key for slot 2 is not for encryption"));
> +      return 0;
> +    }
> +  }
> +  if (keyno == 3) {
> +    if (!(usage & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH))) {
> +      log_error (_("the key for slot 1 is not for signature or authentication"));
> +      return 0;
> +    }
> +  }
> +  return 1;
> +}
> +/* Unattended send a key to a smartcard */
> +void
> +keyedit_quick_keytocard (ctrl_t ctrl, const char *fpr, const char *slot_usage,
> +                      const char *serialno)
> +{
> +  gpg_error_t err;
> +  kbnode_t keyblock;
> +  KEYDB_HANDLE kdbhd;
> +  KBNODE node = NULL;
> +
> +#ifdef HAVE_W32_SYSTEM
> +  /* See keyedit_menu for why we need this.  */
> +  check_trustdb_stale (ctrl);
> +#endif
> +
> +  /* We require a fingerprint because only this uniquely identifies a
> +   * key */
> +  err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
> +  if (err)
> +    {
> +      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
> +      goto leave;
> +    }
> +
> +  for (node = keyblock; node; node = node->next)
> +  {
> +    if (PKT_PUBLIC_KEY == node->pkt->pkttype ||
> +        PKT_PUBLIC_SUBKEY == node->pkt->pkttype ||
> +        PKT_SECRET_KEY == node->pkt->pkttype)
> +    {
> +      PKT_public_key *pk = node->pkt->pkt.public_key;
> +      char hexfpr[2*MAX_FINGERPRINT_LEN+1];
> +      hexfingerprint (pk, hexfpr, sizeof hexfpr);
> +      if (!strcmp(fpr, hexfpr))
> +      {
> +        int keyno = get_keyno_from_slot_usage(slot_usage);
> +        if (!(1 <= keyno && keyno <= 3))
> +        {
> +          log_error (_("slot has to be 1,2,3 is %s"), slot_usage);
> +          goto leave;
> +        }
> +        if (!is_keyno_matching_usage(keyno, pk->pubkey_usage))
> +        {
> +          goto leave;
> +        }
> +        if (send_keytocard(pk, keyno, serialno))
> +        {
> +          log_info(_("keytocard for %s to slot %u successful"), hexfpr, keyno);
> +        }
> +      }
> +    }
> +  }
> +leave:
> +  release_kbnode (keyblock);
> +  keydb_release (kdbhd);
> +}
> +
> 
> 
> static void
> diff --git a/g10/main.h b/g10/main.h
> index 63aec47..c73f682 100644
> --- a/g10/main.h
> +++ b/g10/main.h
> @@ -295,6 +295,9 @@ void keyedit_quick_revuid (ctrl_t ctrl, const char *username,
>                            const char *uidtorev);
> void keyedit_quick_sign (ctrl_t ctrl, const char *fpr,
>                          strlist_t uids, strlist_t locusr, int local);
> +void keyedit_quick_keytocard (ctrl_t ctrl, const char *fpr,
> +                              const char *slot_usage, const char *sm_serial);
> +
> void show_basic_key_info (KBNODE keyblock);
> 
> /*-- keygen.c --*/
> @@ -479,6 +482,7 @@ void card_status (estream_t fp, char *serialno, size_t serialnobuflen);
> void card_edit (ctrl_t ctrl, strlist_t commands);
> gpg_error_t  card_generate_subkey (KBNODE pub_keyblock);
> int  card_store_subkey (KBNODE node, int use);
> +int send_keytocard(PKT_public_key *pk, int keyno, const char *serialno);
> #endif
> 
> #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6))
> -- 
> 2.9.3 (Apple Git-75)
> 
> ----BEGIN PGP MESSAGE-----
> Comment: GPGTools - http://gpgtools.org
> 
> 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
> 1e3KzDbkGw8vyc06XXSCRQCZxuE4hN3kxIgIboDhymHNOYf9cZZMCr1ewwEDCAUW
> rqvEKYyNzI4VOxMJq651ak/CaKnG+0ZPjLFQoYugzb3ZDVPDN4qBllOcHJuOZ/bX
> gREu4ElsQMZxE5BBbqaG6j3GmgrdOq/SMTgPoF2JCxbozkW+Xe3zU2WGbNzkbdHV
> 5d1sxIwa6CSbOshmh2N5sSLhxz/8appJZBPsc5w3EW9LuI2f1VNjgLVONdertzjT
> 88KKU+jE8GqIhb7noJU8Mo0PBCvoA2dcXvj58z7Qz3qmFT5MdYntyjK1bdWtomZ8
> HwjMV0OMhZqTxvjNH7LxTMuqIAtIvkuHZtAft10m+AMgVkRa+mE8X6YqXhvLH7xO
> uLdHonKuL5WS+3rU3v5sayfsgTfaYG+BjSYG8mwyAZcB7H+R+gqH1LblC2dCmD9b
> 5i5NvyaMwCG3DUUs4ecplyuQghCtarYDqEDGTgSewRqubfGWEO75oaYN/f9abdHu
> VJzbyRxsjpvWa4ETlqcOu2IsiCXJ5hnOm9h6RTngVmxIC2aeYwMLOCslk1Dg2ISi
> jw2ccv3evInt6n1gheYIjucUa41UA9FUMxHqI3gKFJ16kJVctUr1urXYRqivkHoa
> mpaNWk3SFfze0tSNcFR/chb+zgtpQwyKh3Za9WfhIXYkIL+GL8BDgkO8FgQ0fA20
> PJpOSSG7UxwdGwHC4siu3dPFKA6WYyQazfS6VRKHG77QRTJ56F03boQVb2kzTkoA
> pBeSFZhLyKYGUj0kOmya0PWMa06yLx8ThI9f0HPRmrUXr/nQ+9dd9JMje4s3f+Zv
> X/mc842z777i7MYvHo/d8a4Lbhw+dXqNcMlv+CvP3nvFufd//I3C2d/J/Medez70
> v/+jP/6VP97/rqMHz3zyq9l3xNJ7nh5Z1m/fMabf++PHvvt/hUvuzN/+wtKbn775
> kYFt/zm07cznRm946+Dak989ZR7//l3vvUf72C3LkvaZk9KTb3Z/+czXv2c8/ejF
> ByIf/vzWfQ+ee23qmPQT+WPv3Xi5k/7h7tOv3/qdnWde/8NnRt5ev7Dd/Ob3rzt3
> /auXPvNU92XvG3t4t7Hjg/tH37gydePlk9f9waXuX754dOSt+x9/5cm9S/kbXnzn
> sasuOvPRkcrkVTu7rto/8Ogjjz0cfW5CWbdZ/GFt5r4dt920zjwVveXM0X/+ys+i
> A8f3Hfn3+Mj4C2Nnzqz9xcMvxk5sO0Rf+Jcv3Hn78QdPf+8j9x77zntqz27+1uv/
> ps+m3h56/O1HH77yg8/9fP3XT//u3x+5e+7P/2Tdjpc2PXE0e/SdWvX4iU8ePGLc
> 9g/PyKfPfeO+Z7cmf/HqrT+9d8dnx7uunn5AVv7q6VeVi9684JVrzj1RflmbvvBb
> T129b/LM9M+ev+fC+7/87Ytf+tcH8nvvfui+V9b9zV1fG/rCTx+687Mf3vpnXzqx
> fPUnvn3yt2966Pfeia259Xl6ee/zL98yZi+e/f2b7//v3JB1R+TSr732kad+NJhZ
> zn/gWO1L13drn3/ut/7oU490U6n3MvfgE4MvfPrkNydvveDRJ3/ztsTRKz7xvjd+
> fOyfTvz1j8bP9ab+8dj4W39X+upbz/385bWXPvDmzkM/2PXEgy/9oP/kuTvuufLu
> Pz3yFx84FD1xavSu/wc=
> =KMn1
> -----END PGP MESSAGE-----
> 
> 
>> On 2 Dec 2016, at 21:45, Meno Abels <meno.abels at adviser.com> wrote:
>> 
>> Hi,
>> 
>> i yesterday, ask about this new option. 
>> And now I used this lazy friday evening to implement the first part. 
>> I tried to figure out how to contribute code and the right coding style. Both I did not
>> had success with. So please be patient with me and give me feedback what i should change
>> to get this in upstream.
>> 
>> I attached the patch from this commit:
>> ce29272e24e7b718b8fca9b84bc728e65f3dea24
>> 
>> I’m not sure how the process with code contribution works in gpg so again please be 
>> patient and give me feedback.
>> 
>> Next i will try to find a solution to the loopback pinentry to pass both password.
>> 
>> Thx in advance 
>> 
>> meno
>> 
>> <0001-gpg-New-option-quick-keytocard.patch><0001-gpg-New-option-quick-keytocard.patch.asc>
>> 
>>> On 1 Dec 2016, at 13:26, Meno Abels <meno.abels at adviser.com> wrote:
>>> 
>>> hello,
>>> 
>>> i tried to invoke from the  --card-edit menu the keytocard function within batch file. But that seams not work.
>>> 
>>> Which is a document feature —card-edit is a interactive tool. 
>>> 
>>> To make it working, I would try to implement —quick-keytocard in the same style like —quick-addkey.
>>> 
>>> I looked around in gpgme and didn’t not found any entry point which allows me to do keytocard.
>>> 
>>> My question is, if i try now to implement quick-keytocard, would you accept my patch? 
>>> 
>>> Or is there any other idea how to send a key from gpg to a smartcard within a batch?
>>> 
>>> I currently only know one bigger obstacle keytocard needs a passphrase and the adminpin both a gather via
>>> pinentry. This leads to a extension to the batch mode command line (--no-tty --pinentry-mode loopback --passphrase-fd).
>>> 
>>> Thx in advance
>>> 
>>> meno
>>> 
>>> 
>>> 
>> 
>> _______________________________________________
>> Gnupg-devel mailing list
>> Gnupg-devel at gnupg.org
>> http://lists.gnupg.org/mailman/listinfo/gnupg-devel
> 




More information about the Gnupg-devel mailing list