Add limited support for NetKey 3.0 cards.
[gnupg.git] / scd / app-nks.c
1 /* app-nks.c - The Telesec NKS card application.
2  * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <time.h>
27
28 #include "scdaemon.h"
29 #include "i18n.h"
30 #include "iso7816.h"
31 #include "app-common.h"
32 #include "tlv.h"
33
34 static struct
35 {
36   int fid;       /* File ID. */
37   int nks_ver;   /* 0 for NKS version 2, 3 for version 3. */
38   int certtype;  /* Type of certificate or 0 if it is not a certificate. */
39   int iskeypair; /* If true has the FID of the correspoding certificate. */
40   int issignkey; /* True if file is a key usable for signing. */
41   int isenckey;  /* True if file is a key usable for decryption. */
42 } filelist[] = {
43   { 0x4531, 0, 0,  0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
44   { 0xC000, 0, 101 },              /* EF_C.NKS.SIG  */
45   { 0x4331, 0, 100 },
46   { 0x4332, 0, 100 },
47   { 0xB000, 0, 110 },              /* EF_PK.RCA.NKS */
48   { 0x45B1, 0, 0,  0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
49   { 0xC200, 0, 101 },              /* EF_C.NKS.ENC  */
50   { 0x43B1, 0, 100 },
51   { 0x43B2, 0, 100 },
52   { 0x4571, 3, 0,  0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
53   { 0xC500, 3, 101 },              /* EF_C.NKS.AUT  */
54   { 0x45B2, 3, 0,  0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
55   { 0xC201, 3, 101 },              /* EF_C.NKS.ENC1024  */
56   { 0 }
57 };
58
59
60
61 /* Object with application (i.e. NKS) specific data.  */
62 struct app_local_s {
63   int nks_version;  /* NKS version.  */
64
65 };
66
67
68
69 \f
70 /* Release local data. */
71 static void
72 do_deinit (app_t app)
73 {
74   if (app && app->app_local)
75     {
76       xfree (app->app_local);
77       app->app_local = NULL;
78     }
79 }
80
81
82 /* Read the file with FID, assume it contains a public key and return
83    its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
84 static gpg_error_t
85 keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
86 {
87   gpg_error_t err;
88   unsigned char grip[20];
89   unsigned char *buffer[2];
90   size_t buflen[2];
91   gcry_sexp_t sexp;
92   int i;
93   
94   err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
95   if (err)
96     return err;
97   err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]);
98   if (err)
99     return err;
100   err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]);
101   if (err)
102     {
103       xfree (buffer[0]);
104       return err;
105     }
106   
107   if (app->app_local->nks_version < 3)
108     {
109       /* Old versions of NKS store the values in a TLV encoded format.
110          We need to do some checks.  */
111       for (i=0; i < 2; i++)
112         {
113           /* Check that the value appears like an integer encoded as
114              Simple-TLV.  We don't check the tag because the tests cards I
115              have use 1 for both, the modulus and the exponent - the
116              example in the documentation gives 2 for the exponent. */
117           if (buflen[i] < 3)
118             err = gpg_error (GPG_ERR_TOO_SHORT);
119           else if (buffer[i][1] != buflen[i]-2 )
120             err = gpg_error (GPG_ERR_INV_OBJ);
121         }
122     }
123
124   if (!err)
125     err = gcry_sexp_build (&sexp, NULL,
126                            "(public-key (rsa (n %b) (e %b)))",
127                            (int)buflen[0]-2, buffer[0]+2,
128                            (int)buflen[1]-2, buffer[1]+2);
129
130   xfree (buffer[0]);
131   xfree (buffer[1]);
132   if (err)
133     return err;
134
135   if (!gcry_pk_get_keygrip (sexp, grip))
136     {
137       err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by
138                                              libgcrypt. */
139     }
140   else
141     {
142       bin2hex (grip, 20, r_gripstr);
143     }
144   gcry_sexp_release (sexp);
145   return err;
146 }
147
148
149
150 static gpg_error_t
151 do_learn_status (app_t app, ctrl_t ctrl)
152 {
153   gpg_error_t err;
154   char ct_buf[100], id_buf[100];
155   int i;
156
157   /* Output information about all useful objects. */
158   for (i=0; filelist[i].fid; i++)
159     {
160       if (filelist[i].nks_ver > app->app_local->nks_version)
161         continue;
162
163       if (filelist[i].certtype)
164         {
165           size_t len;
166
167           len = app_help_read_length_of_cert (app->slot,
168                                               filelist[i].fid, NULL);
169           if (len)
170             {
171               /* FIXME: We should store the length in the application's
172                  context so that a following readcert does only need to
173                  read that many bytes. */
174               sprintf (ct_buf, "%d", filelist[i].certtype);
175               sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
176               send_status_info (ctrl, "CERTINFO",
177                                 ct_buf, strlen (ct_buf), 
178                                 id_buf, strlen (id_buf), 
179                                 NULL, (size_t)0);
180             }
181         }
182       else if (filelist[i].iskeypair)
183         {
184           char gripstr[40+1];
185
186           err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
187           if (err)
188             log_error ("can't get keygrip from FID 0x%04X: %s\n",
189                        filelist[i].fid, gpg_strerror (err));
190           else
191             {
192               sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
193               send_status_info (ctrl, "KEYPAIRINFO",
194                                 gripstr, 40, 
195                                 id_buf, strlen (id_buf), 
196                                 NULL, (size_t)0);
197             }
198         }
199     }
200
201   return 0;
202 }
203
204
205
206
207 /* Read the certificate with id CERTID (as returned by learn_status in
208    the CERTINFO status lines) and return it in the freshly allocated
209    buffer put into CERT and the length of the certificate put into
210    CERTLEN. */
211 static gpg_error_t
212 do_readcert (app_t app, const char *certid,
213              unsigned char **cert, size_t *certlen)
214 {
215   int i, fid;
216   gpg_error_t err;
217   unsigned char *buffer;
218   const unsigned char *p;
219   size_t buflen, n;
220   int class, tag, constructed, ndef;
221   size_t totobjlen, objlen, hdrlen;
222   int rootca = 0;
223
224   *cert = NULL;
225   *certlen = 0;
226   if (strncmp (certid, "NKS-DF01.", 9) ) 
227     return gpg_error (GPG_ERR_INV_ID);
228   certid += 9;
229   if (!hexdigitp (certid) || !hexdigitp (certid+1)
230       || !hexdigitp (certid+2) || !hexdigitp (certid+3) 
231       || certid[4])
232     return gpg_error (GPG_ERR_INV_ID);
233   fid = xtoi_4 (certid);
234   for (i=0; filelist[i].fid; i++)
235     if ((filelist[i].certtype || filelist[i].iskeypair)
236         && filelist[i].fid == fid)
237       break;
238   if (!filelist[i].fid)
239     return gpg_error (GPG_ERR_NOT_FOUND);
240
241   /* If the requested objects is a plain public key, redirect it to
242      the corresponding certificate.  The whole system is a bit messy
243      because we sometime use the key directly or let the caller
244      retrieve the key from the certificate.  The rationale for
245      that is to support not-yet stored certificates. */
246   if (filelist[i].iskeypair)
247     fid = filelist[i].iskeypair;
248
249
250   /* Read the entire file.  fixme: This could be optimized by first
251      reading the header to figure out how long the certificate
252      actually is. */
253   err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
254   if (err)
255     {
256       log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
257       return err;
258     }
259
260   err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
261   if (err)
262     {
263       log_error ("error reading certificate from FID 0x%04X: %s\n",
264                  fid, gpg_strerror (err));
265       return err;
266     }
267   
268   if (!buflen || *buffer == 0xff)
269     {
270       log_info ("no certificate contained in FID 0x%04X\n", fid);
271       err = gpg_error (GPG_ERR_NOT_FOUND);
272       goto leave;
273     }
274
275   /* Now figure something out about the object. */
276   p = buffer;
277   n = buflen;
278   err = parse_ber_header (&p, &n, &class, &tag, &constructed,
279                           &ndef, &objlen, &hdrlen);
280   if (err)
281     goto leave;
282   if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
283     ;
284   else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
285     rootca = 1;
286   else
287     return gpg_error (GPG_ERR_INV_OBJ);
288   totobjlen = objlen + hdrlen;
289   assert (totobjlen <= buflen);
290
291   err = parse_ber_header (&p, &n, &class, &tag, &constructed,
292                           &ndef, &objlen, &hdrlen);
293   if (err)
294     goto leave;
295   
296   if (rootca)
297     ;
298   else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
299     {
300       const unsigned char *save_p;
301   
302       /* The certificate seems to be contained in a userCertificate
303          container.  Skip this and assume the following sequence is
304          the certificate. */
305       if (n < objlen)
306         {
307           err = gpg_error (GPG_ERR_INV_OBJ);
308           goto leave;
309         }
310       p += objlen;
311       n -= objlen;
312       save_p = p;
313       err = parse_ber_header (&p, &n, &class, &tag, &constructed,
314                               &ndef, &objlen, &hdrlen);
315       if (err) 
316         goto leave;
317       if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
318         return gpg_error (GPG_ERR_INV_OBJ);
319       totobjlen = objlen + hdrlen;
320       assert (save_p + totobjlen <= buffer + buflen);
321       memmove (buffer, save_p, totobjlen);
322     }
323   
324   *cert = buffer;
325   buffer = NULL;
326   *certlen = totobjlen;
327
328  leave:
329   xfree (buffer);
330   return err;
331 }
332
333
334 /* Verify the PIN if required.  */
335 static gpg_error_t
336 verify_pin (app_t app,
337             gpg_error_t (*pincb)(void*, const char *, char **),
338             void *pincb_arg)
339 {
340   iso7816_pininfo_t pininfo;
341   int rc;
342
343   /* Note that force_chv1 is never set but we do it here anyway so
344      that other applications may reuse this function.  For example it
345      makes sense to set force_chv1 for German signature law cards.
346      NKS is very similar to the DINSIG draft standard. */
347   if ( app->did_chv1 && !app->force_chv1 ) 
348     return 0;  /* No need to verify it again.  */
349
350   memset (&pininfo, 0, sizeof pininfo);
351   pininfo.mode = 1;
352   pininfo.minlen = 6;
353   pininfo.maxlen = 16;
354
355   if (!opt.disable_keypad
356       && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
357     {
358       rc = pincb (pincb_arg,
359                   _("||Please enter your PIN at the reader's keypad"),
360                   NULL);
361       if (rc)
362         {
363           log_info (_("PIN callback returned error: %s\n"),
364                     gpg_strerror (rc));
365           return rc;
366         }
367  
368       /* Although it is possible to use a local PIN, we use the global
369          PIN for this application.  */
370       rc = iso7816_verify_kp (app->slot, 0, "", 0, &pininfo); 
371       /* Dismiss the prompt. */
372       pincb (pincb_arg, NULL, NULL);
373     }
374   else
375     {
376       char *pinvalue;
377
378       rc = pincb (pincb_arg, "PIN", &pinvalue); 
379       if (rc)
380         {
381           log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
382           return rc;
383         }
384
385       /* The following limits are due to TCOS but also defined in the
386          NKS specs. */
387       if (strlen (pinvalue) < pininfo.minlen)
388         {
389           log_error ("PIN is too short; minimum length is %d\n",
390                      pininfo.minlen);
391           xfree (pinvalue);
392           return gpg_error (GPG_ERR_BAD_PIN);
393         }
394       else if (strlen (pinvalue) > pininfo.maxlen)
395         {
396           log_error ("PIN is too large; maximum length is %d\n",
397                      pininfo.maxlen);
398           xfree (pinvalue);
399           return gpg_error (GPG_ERR_BAD_PIN);
400         }
401
402       /* Although it is possible to use a local PIN, we use the global
403          PIN for this application.  */
404       rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
405       xfree (pinvalue);
406     }
407
408   if (rc)
409     {
410       if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS )
411         log_error (_("the NullPIN has not yet been changed\n"));
412       else
413         log_error ("verify PIN failed\n");
414       return rc;
415     }
416   app->did_chv1 = 1;
417
418   return 0;
419 }
420
421
422
423 /* Create the signature and return the allocated result in OUTDATA.
424    If a PIN is required the PINCB will be used to ask for the PIN;
425    that callback should return the PIN in an allocated buffer and
426    store that in the 3rd argument.  */
427 static gpg_error_t 
428 do_sign (app_t app, const char *keyidstr, int hashalgo,
429          gpg_error_t (*pincb)(void*, const char *, char **),
430          void *pincb_arg,
431          const void *indata, size_t indatalen,
432          unsigned char **outdata, size_t *outdatalen )
433 {
434   static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
435     { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
436       0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
437   static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
438     { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
439       0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
440   int rc, i;
441   int fid;
442   unsigned char data[35];   /* Must be large enough for a SHA-1 digest
443                                + the largest OID _prefix above. */
444
445   if (!keyidstr || !*keyidstr)
446     return gpg_error (GPG_ERR_INV_VALUE);
447   if (indatalen != 20 && indatalen != 16 && indatalen != 35)
448     return gpg_error (GPG_ERR_INV_VALUE);
449
450   /* Check that the provided ID is valid.  This is not really needed
451      but we do it to enforce correct usage by the caller. */
452   if (strncmp (keyidstr, "NKS-DF01.", 9) ) 
453     return gpg_error (GPG_ERR_INV_ID);
454   keyidstr += 9;
455   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
456       || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
457       || keyidstr[4])
458     return gpg_error (GPG_ERR_INV_ID);
459   fid = xtoi_4 (keyidstr);
460   for (i=0; filelist[i].fid; i++)
461     if (filelist[i].iskeypair && filelist[i].fid == fid)
462       break;
463   if (!filelist[i].fid)
464     return gpg_error (GPG_ERR_NOT_FOUND);
465   if (!filelist[i].issignkey)
466     return gpg_error (GPG_ERR_INV_ID);
467
468   /* Prepare the DER object from INDATA. */
469   if (indatalen == 35)
470     {
471       /* Alright, the caller was so kind to send us an already
472          prepared DER object.  Check that it is waht we want and that
473          it matches the hash algorithm. */
474       if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
475         ;
476       else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
477         ;
478       else 
479         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
480       memcpy (data, indata, indatalen);
481     }
482   else
483     {
484       if (hashalgo == GCRY_MD_SHA1)
485         memcpy (data, sha1_prefix, 15);
486       else if (hashalgo == GCRY_MD_RMD160)
487         memcpy (data, rmd160_prefix, 15);
488       else 
489         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
490       memcpy (data+15, indata, indatalen);
491     }
492
493   rc = verify_pin (app, pincb, pincb_arg);
494   if (!rc)
495     rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
496   return rc;
497 }
498
499
500
501
502 /* Decrypt the data in INDATA and return the allocated result in OUTDATA.
503    If a PIN is required the PINCB will be used to ask for the PIN; it
504    should return the PIN in an allocated buffer and put it into PIN.  */
505 static gpg_error_t 
506 do_decipher (app_t app, const char *keyidstr,
507              gpg_error_t (*pincb)(void*, const char *, char **),
508              void *pincb_arg,
509              const void *indata, size_t indatalen,
510              unsigned char **outdata, size_t *outdatalen )
511 {
512   static const unsigned char mse_parm[] = {
513     0x80, 1, 0x10, /* Select algorithm RSA. */
514     0x84, 1, 0x81  /* Select local secret key 1 for decryption. */
515   };
516   int rc, i;
517   int fid;
518
519   if (!keyidstr || !*keyidstr || !indatalen)
520     return gpg_error (GPG_ERR_INV_VALUE);
521
522   /* Check that the provided ID is valid.  This is not really needed
523      but we do it to to enforce correct usage by the caller. */
524   if (strncmp (keyidstr, "NKS-DF01.", 9) ) 
525     return gpg_error (GPG_ERR_INV_ID);
526   keyidstr += 9;
527   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
528       || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
529       || keyidstr[4])
530     return gpg_error (GPG_ERR_INV_ID);
531   fid = xtoi_4 (keyidstr);
532   for (i=0; filelist[i].fid; i++)
533     if (filelist[i].iskeypair && filelist[i].fid == fid)
534       break;
535   if (!filelist[i].fid)
536     return gpg_error (GPG_ERR_NOT_FOUND);
537   if (!filelist[i].isenckey)
538     return gpg_error (GPG_ERR_INV_ID);
539
540   /* Do the TCOS specific MSE. */
541   rc = iso7816_manage_security_env (app->slot, 
542                                     0xC1, 0xB8,
543                                     mse_parm, sizeof mse_parm);
544   if (!rc)
545     rc = verify_pin (app, pincb, pincb_arg);
546   if (!rc)
547     rc = iso7816_decipher (app->slot, indata, indatalen, 0x81,
548                            outdata, outdatalen);
549   return rc;
550 }
551
552
553 /* Handle the PASSWD command.  CHVNOSTR is currently ignored; we
554    always use VHV0.  RESET_MODE is not yet implemented.  */
555 static gpg_error_t 
556 do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, 
557                unsigned int flags,
558                gpg_error_t (*pincb)(void*, const char *, char **),
559                void *pincb_arg)
560 {
561   gpg_error_t err;
562   char *pinvalue;
563   const char *oldpin;
564   size_t oldpinlen;
565
566   (void)ctrl;
567   (void)chvnostr;
568
569   if ((flags & APP_CHANGE_FLAG_RESET))
570     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
571
572   if ((flags & APP_CHANGE_FLAG_NULLPIN))
573     {
574       /* With the nullpin flag, we do not verify the PIN - it would fail
575          if the Nullpin is still set.  */
576       oldpin = "\0\0\0\0\0";
577       oldpinlen = 6;
578     }
579   else
580     {
581       err = verify_pin (app, pincb, pincb_arg);
582       if (err)
583         return err;
584       oldpin = NULL;
585       oldpinlen = 0;
586     }
587
588   /* TRANSLATORS: Do not translate the "|*|" prefixes but
589      keep it at the start of the string.  We need this elsewhere
590      to get some infos on the string. */
591   err = pincb (pincb_arg, _("|N|New PIN"), &pinvalue); 
592   if (err)
593     {
594       log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
595       return err;
596     }
597
598   err = iso7816_change_reference_data (app->slot, 0x00, 
599                                        oldpin, oldpinlen,
600                                        pinvalue, strlen (pinvalue));
601   xfree (pinvalue);
602   return err;
603 }
604
605
606 /* Perform a simple verify operation.  KEYIDSTR should be NULL or empty.  */
607 static gpg_error_t 
608 do_check_pin (app_t app, const char *keyidstr,
609               gpg_error_t (*pincb)(void*, const char *, char **),
610               void *pincb_arg)
611 {
612   (void)keyidstr;
613   return verify_pin (app, pincb, pincb_arg);
614 }
615
616
617 /* Return the version of the NKS application.  */
618 static int
619 get_nks_version (int slot)
620 {
621   unsigned char *result = NULL;
622   size_t resultlen;
623   int type;
624
625   if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0, 
626                            &result, &resultlen))
627     return 2; /* NKS 2 does not support this command.  */
628   
629   /* Example value:    04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00
630                        vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx
631      vendor (Philips) -+  |  |                 |  |  |  |           |
632      chip type -----------+  |                 |  |  |  |           |
633      chip id ----------------+                 |  |  |  |           |
634      card type (3 - tcos 3) -------------------+  |  |  |           |
635      OS version of card type ---------------------+  |  |           |
636      OS release of card type ------------------------+  |           |
637      OS vendor internal version ------------------------+           |
638      RFU -----------------------------------------------------------+
639   */
640   if (resultlen < 16)
641     type = 0;  /* Invalid data returned.  */
642   else
643     type = result[8];
644   xfree (result);
645
646   return type;
647 }
648
649
650 /* Select the NKS application.  */
651 gpg_error_t
652 app_select_nks (app_t app)
653 {
654   static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
655   int slot = app->slot;
656   int rc;
657   
658   rc = iso7816_select_application (slot, aid, sizeof aid, 0);
659   if (!rc)
660     {
661       app->apptype = "NKS";
662
663       app->app_local = xtrycalloc (1, sizeof *app->app_local);
664       if (!app->app_local)
665         {
666           rc = gpg_error (gpg_err_code_from_errno (errno));
667           goto leave;
668         }
669
670       app->app_local->nks_version = get_nks_version (slot);
671       if (opt.verbose)
672         log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
673
674       app->fnc.deinit = do_deinit;
675       app->fnc.learn_status = do_learn_status;
676       app->fnc.readcert = do_readcert;
677       app->fnc.getattr = NULL;
678       app->fnc.setattr = NULL;
679       app->fnc.genkey = NULL;
680       app->fnc.sign = do_sign;
681       app->fnc.auth = NULL;
682       app->fnc.decipher = do_decipher;
683       app->fnc.change_pin = do_change_pin;
684       app->fnc.check_pin = do_check_pin;
685    }
686
687  leave:
688   if (rc)
689     do_deinit (app);
690   return rc;
691 }
692
693