support PIN modification
[gnuk/gnuk.git] / src / openpgp.c
1 /*
2  * openpgp.c -- OpenPGP card protocol support
3  *
4  * Copyright (C) 2010, 2011 Free Software Initiative of Japan
5  * Author: NIIBE Yutaka <gniibe@fsij.org>
6  *
7  * This file is a part of Gnuk, a GnuPG USB Token implementation.
8  *
9  * Gnuk is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Gnuk is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "config.h"
25 #include "ch.h"
26 #include "hal.h"
27 #include "gnuk.h"
28 #include "openpgp.h"
29 #include "polarssl/config.h"
30 #include "polarssl/sha1.h"
31
32 #define INS_NOP                                 0x00
33 #define INS_VERIFY                              0x20
34 #define INS_CHANGE_REFERENCE_DATA               0x24
35 #define INS_PSO                                 0x2a
36 #define INS_RESET_RETRY_COUNTER                 0x2c
37 #define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR    0x47
38 #define INS_INTERNAL_AUTHENTICATE               0x88
39 #define INS_SELECT_FILE                         0xa4
40 #define INS_READ_BINARY                         0xb0
41 #define INS_GET_DATA                            0xca
42 #define INS_PUT_DATA                            0xda
43 #define INS_PUT_DATA_ODD                        0xdb    /* For key import */
44
45 static const uint8_t
46 select_file_TOP_result[] __attribute__ ((aligned (1))) = {
47   0x00, 0x00,                   /* unused */
48   0x0b, 0x10,                   /* number of bytes in this directory */
49   0x3f, 0x00,                   /* field of selected file: MF, 3f00 */
50   0x38,                 /* it's DF */
51   0xff,                 /* unused */
52   0xff, 0x44, 0x44,     /* access conditions */
53   0x01,                 /* status of the selected file (OK, unblocked) */
54   0x05,                 /* number of bytes of data follow */
55     0x03,                       /* Features: unused */
56     0x01,                       /* number of subdirectories (OpenPGP) */
57     0x01,                       /* number of elementary files (SerialNo) */
58     0x00,                       /* number of secret codes */
59     0x00,                       /* Unused */
60   0x00, 0x00            /* PIN status: OK, PIN blocked?: No */
61 };
62
63 void
64 write_res_apdu (const uint8_t *p, int len, uint8_t sw1, uint8_t sw2)
65 {
66   res_APDU_size = 2 + len;
67   if (len)
68     memcpy (res_APDU, p, len);
69   res_APDU[len] = sw1;
70   res_APDU[len+1] = sw2;
71 }
72
73 #define FILE_NONE       0
74 #define FILE_DF_OPENPGP 1
75 #define FILE_MF         2
76 #define FILE_EF_DIR     3
77 #define FILE_EF_SERIAL  4
78
79 static uint8_t file_selection;
80
81 static void
82 gpg_init (void)
83 {
84   const uint8_t *flash_data_start;
85
86   file_selection = FILE_NONE;
87   flash_data_start = flash_init ();
88   gpg_data_scan (flash_data_start);
89 }
90
91 static void
92 gpg_fini (void)
93 {
94   ac_fini ();
95 }
96
97 /* 
98  * Invoke the thread PIN_MAIN, and let user input PIN string.
99  * Return length of the string.
100  * The string itself is in PIN_INPUT_BUFFER.
101  */
102 static int
103 get_pinpad_input (int msg_code)
104 {
105   Thread *t;
106
107   t = chThdCreateFromHeap (NULL, THD_WA_SIZE (128),
108                            NORMALPRIO, pin_main, (void *)msg_code);
109   if (t == NULL)
110     return -1;
111   else
112     {
113       chThdWait (t);
114       return pin_input_len;
115     }
116 }
117
118 static void
119 cmd_verify (void)
120 {
121   int len;
122   uint8_t p2 = cmd_APDU[3];
123   int r;
124   int data_start = 5;
125   const uint8_t *pw;
126
127   DEBUG_INFO (" - VERIFY\r\n");
128   DEBUG_BYTE (p2);
129
130 #if defined(PINPAD_SUPPORT)
131   if (cmd_APDU_size == 4)
132     /* Verification with pinpad */
133     {
134       len = get_pinpad_input (PIN_INPUT_CURRENT);
135       if (len < 0)
136         {
137           GPG_ERROR ();
138           return;
139         }
140       else
141         pw = pin_input_buffer;
142     }
143   else
144 #endif
145     {
146       len = cmd_APDU[4];
147       if (len == 0)                     /* extended length */
148         {
149           len = (cmd_APDU[5]<<8) | cmd_APDU[6];
150           data_start = 7;
151         }
152
153       pw = &cmd_APDU[data_start];
154     }
155
156
157   if (p2 == 0x81)
158     r = verify_pso_cds (pw, len);
159   else if (p2 == 0x82)
160     r = verify_other (pw, len);
161   else
162     r = verify_admin (pw, len);
163
164   if (r < 0)
165     {
166       DEBUG_INFO ("failed\r\n");
167       GPG_SECURITY_FAILURE ();
168     }
169   else if (r == 0)
170     {
171       DEBUG_INFO ("blocked\r\n");
172       GPG_SECURITY_AUTH_BLOCKED ();
173     }
174   else
175     {
176       DEBUG_INFO ("good\r\n");
177       GPG_SUCCESS ();
178     }
179 }
180
181 int
182 gpg_change_keystring (int who_old, const uint8_t *old_ks,
183                       int who_new, const uint8_t *new_ks)
184 {
185   int r;
186   int prv_keys_exist = 0;
187
188   r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks);
189   if (r < 0)
190     return r;
191
192   if (r > 0)
193     prv_keys_exist++;
194
195   r = gpg_do_chks_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks,
196                           who_new, new_ks);
197   if (r < 0)
198     return -2;
199
200   r = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks);
201   if (r < 0)
202     return r;
203
204   if (r > 0)
205     prv_keys_exist++;
206
207   r = gpg_do_chks_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks,
208                           who_new, new_ks);
209   if (r < 0)
210     return -2;
211
212   r = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks);
213   if (r < 0)
214     return r;
215
216   if (r > 0)
217     prv_keys_exist++;
218
219   r = gpg_do_chks_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks,
220                           who_new, new_ks);
221   if (r < 0)
222     return -2;
223
224   if (prv_keys_exist)
225     return 1;
226   else
227     return 0;
228 }
229
230 static void
231 cmd_change_password (void)
232 {
233   uint8_t old_ks[KEYSTRING_MD_SIZE];
234   uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
235   uint8_t *new_ks = &new_ks0[1];
236   uint8_t p2 = cmd_APDU[3];
237   int len;
238   const uint8_t *pw;
239   const uint8_t *newpw;
240   int pw_len, newpw_len;
241   int who = p2 - 0x80;
242   int r;
243
244   DEBUG_INFO ("Change PW\r\n");
245   DEBUG_BYTE (who);
246
247 #if defined(PINPAD_SUPPORT)
248   if (cmd_APDU_size == 4)
249     /* Modification with pinpad */
250     {
251       pw_len = get_pinpad_input (PIN_INPUT_CURRENT);
252       if (pw_len < 0)
253         {
254           GPG_ERROR ();
255           return;
256         }
257
258       pw = &cmd_APDU[5];
259       memcpy (&cmd_APDU[5], pin_input_buffer, pw_len);
260       newpw = pw + pw_len;
261
262       newpw_len = get_pinpad_input (PIN_INPUT_NEW);
263       if (newpw_len < 0)
264         {
265           GPG_ERROR ();
266           return;
267         }
268
269       memcpy (&cmd_APDU[5]+pw_len, pin_input_buffer, newpw_len);
270
271       len = get_pinpad_input (PIN_INPUT_CONFIRM);
272       if (len < 0)
273         {
274           GPG_ERROR ();
275           return;
276         }
277
278       if (len != newpw_len || memcmp (newpw, pin_input_buffer, len) != 0)
279         {
280           GPG_SECURITY_FAILURE ();
281           return;
282         }
283
284       len = cmd_APDU[4] = pw_len + newpw_len;
285     }
286   else
287 #endif
288     {
289       len = cmd_APDU[4];
290       pw = &cmd_APDU[5];
291       if (len == 0)                     /* extended length */
292         {
293           len = (cmd_APDU[5]<<8) | cmd_APDU[6];
294           pw += 2;
295         }
296     }
297
298   if (who == BY_USER)                   /* PW1 */
299     {
300       const uint8_t *pk = gpg_do_read_simple (NR_DO_KEYSTRING_PW1);
301
302       if (pk == NULL)
303         {
304           if (len < (int)strlen (OPENPGP_CARD_INITIAL_PW1))
305             {
306               DEBUG_INFO ("permission denied.\r\n");
307               GPG_SECURITY_FAILURE ();
308               return;
309             }
310
311           pw_len = strlen (OPENPGP_CARD_INITIAL_PW1);
312           newpw = pw + pw_len;
313           newpw_len = len - pw_len;
314         }
315       else
316         {
317           pw_len = pk[0];
318           newpw = pw + pw_len;
319           newpw_len = len - pw_len;
320         }
321     }
322   else                          /* PW3 (0x83) */
323     {
324       pw_len = verify_admin_0 (pw, len, -1);
325
326       if (pw_len < 0)
327         {
328           DEBUG_INFO ("permission denied.\r\n");
329           GPG_SECURITY_FAILURE ();
330           return;
331         }
332       else if (pw_len == 0)
333         {
334           DEBUG_INFO ("blocked.\r\n");
335           GPG_SECURITY_AUTH_BLOCKED ();
336           return;
337         }
338       else
339         {
340           newpw = pw + pw_len;
341           newpw_len = len - pw_len;
342           gpg_set_pw3 (newpw, newpw_len);
343         }
344     }
345
346   sha1 (pw, pw_len, old_ks);
347   sha1 (newpw, newpw_len, new_ks);
348   new_ks0[0] = newpw_len;
349
350   r = gpg_change_keystring (who, old_ks, who, new_ks);
351   if (r < -2)
352     {
353       DEBUG_INFO ("memory error.\r\n");
354       GPG_MEMORY_FAILURE ();
355     }
356   else if (r < 0)
357     {
358       DEBUG_INFO ("security error.\r\n");
359       GPG_SECURITY_FAILURE ();
360     }
361   else if (r == 0 && who == BY_USER)    /* no prvkey */
362     {
363       gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KEYSTRING_SIZE_PW1);
364       ac_reset_pso_cds ();
365       ac_reset_other ();
366       gpg_reset_pw_err_counter (PW_ERR_PW1);
367       DEBUG_INFO ("Changed DO_KEYSTRING_PW1.\r\n");
368       GPG_SUCCESS ();
369     }
370   else if (r > 0 && who == BY_USER)
371     {
372       gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, 1);
373       ac_reset_pso_cds ();
374       ac_reset_other ();
375       gpg_reset_pw_err_counter (PW_ERR_PW1);
376       DEBUG_INFO ("Changed length of DO_KEYSTRING_PW1.\r\n");
377       GPG_SUCCESS ();
378     }
379   else                          /* r >= 0 && who == BY_ADMIN */
380     {
381       DEBUG_INFO ("done.\r\n");
382       ac_reset_admin ();
383       gpg_reset_pw_err_counter (PW_ERR_PW3);
384       GPG_SUCCESS ();
385     }
386 }
387
388 static void
389 cmd_reset_user_password (void)
390 {
391   uint8_t p1 = cmd_APDU[2];
392   int len = cmd_APDU[4];
393   const uint8_t *pw = &cmd_APDU[5];
394   const uint8_t *newpw;
395   int pw_len, newpw_len;
396   int r;
397   uint8_t new_ks0[KEYSTRING_MD_SIZE+1];
398   uint8_t *new_ks = &new_ks0[1];
399
400   DEBUG_INFO ("Reset PW1\r\n");
401   DEBUG_BYTE (p1);
402
403   if (len == 0)                 /* extended length */
404     {
405       len = (cmd_APDU[5]<<8) | cmd_APDU[6];
406       pw += 2;
407     }
408
409   if (p1 == 0x00)               /* by User with Reseting Code */
410     {
411       const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC);
412       uint8_t old_ks[KEYSTRING_MD_SIZE];
413
414       if (gpg_passwd_locked (PW_ERR_RC))
415         {
416           DEBUG_INFO ("blocked.\r\n");
417           GPG_SECURITY_AUTH_BLOCKED ();
418           return;
419         }
420
421       if (ks_rc == NULL)
422         {
423           DEBUG_INFO ("security error.\r\n");
424           GPG_SECURITY_FAILURE ();
425           return;
426         }
427
428       pw_len = ks_rc[0];
429       newpw = pw + pw_len;
430       newpw_len = len - pw_len;
431       sha1 (pw, pw_len, old_ks);
432       sha1 (newpw, newpw_len, new_ks);
433       new_ks0[0] = newpw_len;
434       r = gpg_change_keystring (BY_RESETCODE, old_ks, BY_USER, new_ks);
435       if (r < -2)
436         {
437           DEBUG_INFO ("memory error.\r\n");
438           GPG_MEMORY_FAILURE ();
439         }
440       else if (r < 0)
441         {
442         sec_fail:
443           DEBUG_INFO ("failed.\r\n");
444           gpg_increment_pw_err_counter (PW_ERR_RC);
445           GPG_SECURITY_FAILURE ();
446         }
447       else if (r == 0)
448         {
449           if (memcmp (ks_rc+1, old_ks, KEYSTRING_MD_SIZE) != 0)
450             goto sec_fail;
451           DEBUG_INFO ("done (no prvkey).\r\n");
452           gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0,
453                                KEYSTRING_SIZE_PW1);
454           ac_reset_pso_cds ();
455           ac_reset_other ();
456           gpg_reset_pw_err_counter (PW_ERR_RC);
457           gpg_reset_pw_err_counter (PW_ERR_PW1);
458           GPG_SUCCESS ();
459         }
460       else
461         {
462           DEBUG_INFO ("done.\r\n");
463           ac_reset_pso_cds ();
464           ac_reset_other ();
465           gpg_reset_pw_err_counter (PW_ERR_RC);
466           gpg_reset_pw_err_counter (PW_ERR_PW1);
467           GPG_SUCCESS ();
468         }
469     }
470   else                          /* by Admin (p1 == 0x02) */
471     {
472       const uint8_t *old_ks = keystring_md_pw3;
473
474       if (!ac_check_status (AC_ADMIN_AUTHORIZED))
475         {
476           DEBUG_INFO ("permission denied.\r\n");
477           GPG_SECURITY_FAILURE ();
478           return;
479         }
480
481       newpw_len = len;
482       newpw = pw;
483       sha1 (newpw, newpw_len, new_ks);
484       new_ks0[0] = newpw_len;
485       r = gpg_change_keystring (BY_ADMIN, old_ks, BY_USER, new_ks);
486       if (r < -2)
487         {
488           DEBUG_INFO ("memory error.\r\n");
489           GPG_MEMORY_FAILURE ();
490         }
491       else if (r < 0)
492         {
493           DEBUG_INFO ("security error.\r\n");
494           GPG_SECURITY_FAILURE ();
495         }
496       else if (r == 0)
497         {
498           DEBUG_INFO ("done (no privkey).\r\n");
499           gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0,
500                                KEYSTRING_SIZE_PW1);
501           ac_reset_pso_cds ();
502           ac_reset_other ();
503           gpg_reset_pw_err_counter (PW_ERR_PW1);
504           GPG_SUCCESS ();
505         }
506       else
507         {
508           DEBUG_INFO ("done.\r\n");
509           ac_reset_pso_cds ();
510           ac_reset_other ();
511           gpg_reset_pw_err_counter (PW_ERR_PW1);
512           GPG_SUCCESS ();
513         }
514     }
515 }
516
517 static void
518 cmd_put_data (void)
519 {
520   uint8_t *data;
521   uint16_t tag;
522   int len;
523
524   DEBUG_INFO (" - PUT DATA\r\n");
525
526   if (file_selection != FILE_DF_OPENPGP)
527     GPG_NO_RECORD();
528
529   tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
530   len = cmd_APDU_size - 5;
531   data = &cmd_APDU[5];
532   if (len >= 256)
533     /* extended Lc */
534     {
535       data += 2;
536       len -= 2;
537     }
538
539   gpg_do_put_data (tag, data, len);
540 }
541
542 static void
543 cmd_pgp_gakp (void)
544 {
545   DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n");
546   DEBUG_BYTE (cmd_APDU[2]);
547
548   if (cmd_APDU[2] == 0x81)
549     /* Get public key */
550     gpg_do_public_key (cmd_APDU[7]);
551   else
552     {                                   /* Generate key pair */
553       if (!ac_check_status (AC_ADMIN_AUTHORIZED))
554         GPG_SECURITY_FAILURE ();
555
556       /* XXX: Not yet supported */
557       GPG_ERROR ();
558     }
559 }
560
561 static void
562 cmd_read_binary (void)
563 {
564   DEBUG_INFO (" - Read binary\r\n");
565
566   if (file_selection == FILE_EF_SERIAL)
567     {
568       if (cmd_APDU[3] >= 6)
569         GPG_BAD_P0_P1 ();
570       else
571         {
572           gpg_do_get_data (0x004f, 1); /* AID */
573           res_APDU[0] = 0x5a;
574         }
575     }
576   else
577     GPG_NO_RECORD();
578 }
579
580 static void
581 cmd_select_file (void)
582 {
583   if (cmd_APDU[2] == 4) /* Selection by DF name */
584     {
585       DEBUG_INFO (" - select DF by name\r\n");
586
587       /*
588        * P2 == 0, LC=6, name = D2 76 00 01 24 01
589        */
590
591       file_selection = FILE_DF_OPENPGP;
592       GPG_SUCCESS ();
593     }
594   else if (cmd_APDU[4] == 2
595            && cmd_APDU[5] == 0x2f
596            && cmd_APDU[6] == 0x02)
597     {
598       DEBUG_INFO (" - select 0x2f02 EF\r\n");
599       /*
600        * MF.EF-GDO -- Serial number of the card and name of the owner
601        */
602       GPG_SUCCESS ();
603       file_selection = FILE_EF_SERIAL;
604     }
605   else if (cmd_APDU[4] == 2
606            && cmd_APDU[5] == 0x3f
607            && cmd_APDU[6] == 0x00)
608     {
609       DEBUG_INFO (" - select ROOT MF\r\n");
610       if (cmd_APDU[3] == 0x0c)
611         {
612           GPG_SUCCESS ();
613         }
614       else
615         {
616           write_res_apdu (select_file_TOP_result,
617                           sizeof (select_file_TOP_result), 0x90, 0x00);
618           res_APDU[2] = (data_objects_number_of_bytes & 0xff);
619           res_APDU[3] = (data_objects_number_of_bytes >> 8);
620         }
621
622       file_selection = FILE_MF;
623     }
624   else
625     {
626       DEBUG_INFO (" - select ?? \r\n");
627
628       file_selection = FILE_NONE;
629       GPG_NO_FILE();
630     }
631 }
632
633 static void
634 cmd_get_data (void)
635 {
636   uint16_t tag = ((cmd_APDU[2]<<8) | cmd_APDU[3]);
637
638   DEBUG_INFO (" - Get Data\r\n");
639
640   if (file_selection != FILE_DF_OPENPGP)
641     GPG_NO_RECORD();
642
643   gpg_do_get_data (tag, 0);
644 }
645
646 static void
647 cmd_pso (void)
648 {
649   int len = cmd_APDU[4];
650   int data_start = 5;
651   int r;
652
653   if (len == 0)
654     {
655       len = (cmd_APDU[5]<<8) | cmd_APDU[6];
656       data_start = 7;
657     }
658
659   DEBUG_INFO (" - PSO: ");
660   DEBUG_WORD ((uint32_t)&r);
661
662   if (cmd_APDU[2] == 0x9e && cmd_APDU[3] == 0x9a)
663     {
664       if (!ac_check_status (AC_PSO_CDS_AUTHORIZED))
665         {
666           DEBUG_INFO ("security error.");
667           GPG_SECURITY_FAILURE ();
668           return;
669         }
670
671       if (cmd_APDU_size != 7 + 35 + 2     /* SHA1 / RIPEMD-160 */
672           /* Header (with Extended Lc)=7, size of digestInfo, and Le=2-byte */
673           && cmd_APDU_size != 7 + 47 + 2  /* SHA224 */
674           && cmd_APDU_size != 7 + 51 + 2  /* SHA256 */
675           && cmd_APDU_size != 7 + 67 + 2  /* SHA384 */
676           && cmd_APDU_size != 7 + 83 + 2) /* SHA512 */
677         {
678           DEBUG_INFO (" wrong length: ");
679           DEBUG_SHORT (cmd_APDU_size);
680           GPG_ERROR ();
681         }
682       else
683         {
684           DEBUG_SHORT (len);  /* Should be cmd_APDU_size - 8 [- 1] */
685
686           r = rsa_sign (&cmd_APDU[data_start], res_APDU, len,
687                         &kd[GPG_KEY_FOR_SIGNING]);
688           if (r < 0)
689             {
690               ac_reset_pso_cds ();
691               GPG_ERROR ();
692             }
693           else
694             {                   /* Success */
695               if (gpg_get_pw1_lifetime () == 0)
696                 ac_reset_pso_cds ();
697
698               gpg_increment_digital_signature_counter ();
699             }
700         }
701     }
702   else if (cmd_APDU[2] == 0x80 && cmd_APDU[3] == 0x86)
703     {
704       DEBUG_SHORT (len);
705
706       if (gpg_passwd_locked (PW_ERR_PW1)
707           || !ac_check_status (AC_OTHER_AUTHORIZED))
708         {
709           DEBUG_INFO ("security error.");
710           GPG_SECURITY_FAILURE ();
711           return;
712         }
713
714       /* Skip padding 0x00 */
715       data_start++;
716       len--;
717       r = rsa_decrypt (&cmd_APDU[data_start], res_APDU, len,
718                        &kd[GPG_KEY_FOR_DECRYPTION]);
719       if (r < 0)
720         GPG_ERROR ();
721     }
722   else
723     {                           /* XXX: not yet supported */
724       DEBUG_INFO (" - ??");
725       DEBUG_BYTE (cmd_APDU[2]);
726       DEBUG_INFO (" - ??");
727       DEBUG_BYTE (cmd_APDU[3]);
728       GPG_ERROR ();
729     }
730
731   DEBUG_INFO ("PSO done.\r\n");
732 }
733
734 static void
735 cmd_internal_authenticate (void)
736 {
737   int len = cmd_APDU[4];
738   int data_start = 5;
739   int r;
740
741   if (len == 0)
742     {
743       len = (cmd_APDU[5]<<8) | cmd_APDU[6];
744       data_start = 7;
745     }
746
747   DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n");
748
749   if (cmd_APDU[2] == 0x00 && cmd_APDU[3] == 0x00)
750     {
751       DEBUG_SHORT (len);
752
753       if (gpg_passwd_locked (PW_ERR_PW1)
754           || !ac_check_status (AC_OTHER_AUTHORIZED))
755         {
756           DEBUG_INFO ("security error.");
757           GPG_SECURITY_FAILURE ();
758           return;
759         }
760
761       r = rsa_sign (&cmd_APDU[data_start], res_APDU, len,
762                     &kd[GPG_KEY_FOR_AUTHENTICATION]);
763       if (r < 0)
764         GPG_ERROR ();
765     }
766   else
767     {
768       DEBUG_INFO (" - ??");
769       DEBUG_BYTE (cmd_APDU[2]);
770       DEBUG_INFO (" - ??");
771       DEBUG_BYTE (cmd_APDU[3]);
772       GPG_ERROR ();
773     }
774
775   DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n");
776 }
777
778 struct command
779 {
780   uint8_t command;
781   void (*cmd_handler) (void);
782 };
783
784 const struct command cmds[] = {
785   { INS_VERIFY, cmd_verify },
786   { INS_CHANGE_REFERENCE_DATA, cmd_change_password },
787   { INS_PSO, cmd_pso },
788   { INS_RESET_RETRY_COUNTER, cmd_reset_user_password },
789   { INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp },
790   { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate },
791   { INS_SELECT_FILE, cmd_select_file },
792   { INS_READ_BINARY, cmd_read_binary },
793   { INS_GET_DATA, cmd_get_data },
794   { INS_PUT_DATA, cmd_put_data },
795   { INS_PUT_DATA_ODD, cmd_put_data },
796 };
797 #define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command)))
798
799 static void
800 process_command_apdu (void)
801 {
802   int i;
803   uint8_t cmd = cmd_APDU[1];
804
805   for (i = 0; i < NUM_CMDS; i++)
806     if (cmds[i].command == cmd)
807       break;
808
809   if (i < NUM_CMDS)
810     cmds[i].cmd_handler ();
811   else
812     {
813       DEBUG_INFO (" - ??");
814       DEBUG_BYTE (cmd);
815       GPG_NO_INS ();
816     }
817 }
818
819 msg_t
820 GPGthread (void *arg)
821 {
822   Thread *icc_thread = (Thread *)arg;
823
824   gpg_init ();
825
826   chEvtClear (ALL_EVENTS);
827
828   while (!chThdShouldTerminate ())
829     {
830       eventmask_t m;
831
832       m = chEvtWaitOne (ALL_EVENTS);
833
834       DEBUG_INFO ("GPG!: ");
835       DEBUG_WORD ((uint32_t)&m);
836
837       if (icc_data_size != 0)
838         {
839           process_command_apdu ();
840           chEvtSignal (icc_thread, EV_EXEC_FINISHED);
841         }
842     }
843
844   gpg_fini ();
845   return 0;
846 }