scd: For NetKey cards READKEY with keygrip fails
Open, NormalPublic

Description

This is with master. This does actually happen when trying to generate an OpenPGP public key for NetKey card keys with gpg --quick-gen-key --yes nks@example.net card, but it's easier/faster to reproduce as follows (the first few SCD commands may not be necessary):
Plug in card reader (I have an IDENTIV SPR332 V2) and insert NetKey card. Then run

$ gpg-connect-agent "SCD SERIALNO" /bye
gpg-connect-agent: no running gpg-agent - starting '/opt/gnupg/master/bin/gpg-agent'
gpg-connect-agent: waiting for the agent to come up ... (5s)
gpg-connect-agent: connection to the agent established
S SERIALNO 89490173300026616520
OK

$ gpg-connect-agent "SCD LEARN --force" /bye
S READER 04E6:E003:51271834208013:0
S SERIALNO 89490173300026616520
S APPTYPE nks
S APPVERSION 3
S CHV-STATUS -4+0+-4+0
S KEYPAIRINFO 39400430E38BB96F105B740A7119FE113578B59D NKS-NKS3.4531 sa -
S CERTINFO 101 NKS-NKS3.C000
S CERTINFO 110 NKS-NKS3.B000
S KEYPAIRINFO 42C3CA6F9D7A725A59DDE06B16B61E88C62777C4 NKS-NKS3.45B1 e -
S CERTINFO 101 NKS-NKS3.C200
S KEYPAIRINFO 20E7CB1D5299669CABF29B103C692AB34CB03528 NKS-NKS3.45B2 e -
S CERTINFO 101 NKS-NKS3.C201
S KEYPAIRINFO A69B0D3796EE33E4426E5CE4B6BEEE5F1209FBA4 NKS-SIGG.4531 se -
S CERTINFO 101 NKS-SIGG.C000
OK

$ gpg-connect-agent "SCD READKEY --info-only -- NKS-NKS3.4531" /bye
S KEYPAIRINFO 39400430E38BB96F105B740A7119FE113578B59D NKS-NKS3.4531 - - rsa2048
OK

$ gpg-connect-agent "SCD READKEY --info-only -- 39400430E38BB96F105B740A7119FE113578B59D" /bye
ERR 100663414 Invalid ID <SCD>

While trying to debug this I noticed a few things.

In cmd_readkey() (scd/command.c) a check for rc seems to be missing which causes the above bogus error message "Invalid ID". I propose the following patch:

diff --git a/scd/command.c b/scd/command.c
index c1cded6b1..47912adfe 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -735,6 +739,8 @@ cmd_readkey (assuan_context_t ctx, char *line)
     }
   else
     rc = gpg_error (GPG_ERR_NO_SECKEY);
+  if (rc)
+    goto leave;
 
   if (opt_nokey)
     ;

After applying this patch the above fails with

ERR 100663313 No secret key <SCD>

Further debugging revealed that filelist in do_with_keygrip() (scd/app-nks.c) seems to contain the fourth key (A69B0D3796EE33E4426E5CE4B6BEEE5F1209FBA4) twice, but not the first key (39400430E38BB96F105B740A7119FE113578B59D). I've added

log_info ("do_with_keygrip (app-nks.c): keygripstr: %s", keygripstr);

after

err = keygripstr_from_pk_file (app, filelist[idx].fid,
                               filelist[idx].iskeypair, keygripstr,
                               NULL, NULL);

and I got the following debug output:

2020-11-20 14:54:49 scdaemon[10384] do_with_keygrip (app-nks.c): keygripstr: A69B0D3796EE33E4426E5CE4B6BEEE5F1209FBA4
2020-11-20 14:54:49 scdaemon[10384] do_with_keygrip (app-nks.c): keygripstr: 42C3CA6F9D7A725A59DDE06B16B61E88C62777C4
2020-11-20 14:54:49 scdaemon[10384] do_with_keygrip (app-nks.c): keygripstr: 20E7CB1D5299669CABF29B103C692AB34CB03528
2020-11-20 14:54:49 scdaemon[10384] nks: switching to SigG
2020-11-20 14:54:50 scdaemon[10384] do_with_keygrip (app-nks.c): keygripstr: A69B0D3796EE33E4426E5CE4B6BEEE5F1209FBA4

At this point I stopped looking further into the code.

BTW, READKEY with the keygrip of the second key also fails, but somewhat differently. It fails with error log app_readcert failed: Invalid ID.

With 2.2.23 gpg --quick-gen-key --yes nks@example.net card works except that after entering the PIN it fails with "Conditions of use not satisfied" maybe because "the NullPIN has not yet been changed".

gniibe added a subscriber: gniibe.Wed, Nov 25, 3:48 AM

For the first issue, I pushed the change in rGc3a20c88fb30: scd: Fix an error return for READKEY..

For the second issue, I think that the logic to identify a key is wrong for the cache.
Please try:

diff --git a/scd/app-nks.c b/scd/app-nks.c
index caf686316..7ed3be75f 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -292,7 +292,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr,
   struct fid_cache_s *ci;
 
   for (ci = app->app_local->fid_cache; ci; ci = ci->next)
-    if (ci->fid && ci->fid == pkfid)
+    if (ci->fid && ((cfid && ci->fid == cfid) || (!cfid && ci->fid == pkfid)))
       {
         if (!ci->got_keygrip)
           return gpg_error (GPG_ERR_NOT_FOUND);
@@ -455,7 +455,8 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr,
 
       /* FIXME: We need to implement not_found caching.  */
       for (ci = app->app_local->fid_cache; ci; ci = ci->next)
-        if (ci->fid && ci->fid == pkfid)
+        if (ci->fid
+            && ((cfid && ci->fid == cfid) || (!cfid && ci->fid == pkfid)))
           {
             /* Update the keygrip.  */
             memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
@@ -476,7 +477,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr,
             ; /* Out of memory - it is a cache, so we ignore it.  */
           else
             {
-              ci->fid = pkfid;
+              ci->fid = cfid? cfid : pkfid;
               memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
               ci->algo = algo;
               ci->got_keygrip = 1;
gniibe triaged this task as Normal priority.Wed, Nov 25, 3:48 AM