1 /* app-nks.c - The Telesec NKS 2.0 card application.
2 * Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
31 #include "app-common.h"
36 int fid
; /* File ID. */
37 int certtype
; /* Type of certificate or 0 if it is not a certificate. */
38 int iskeypair
; /* If true has the FID of the correspoding certificate. */
39 int issignkey
; /* True if file is a key usable for signing. */
40 int isenckey
; /* True if file is a key usable for decryption. */
42 { 0x4531, 0, 0xC000, 1, 0 },
47 { 0x45B1, 0, 0xC200, 0, 1 },
56 /* Read the file with FID, assume it contains a public key and return
57 its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
59 keygripstr_from_pk_file (int slot
, int fid
, char *r_gripstr
)
62 unsigned char grip
[20];
63 unsigned char *buffer
[2];
68 err
= iso7816_select_file (slot
, fid
, 0, NULL
, NULL
);
71 err
= iso7816_read_record (slot
, 1, 1, 0, &buffer
[0], &buflen
[0]);
74 err
= iso7816_read_record (slot
, 2, 1, 0, &buffer
[1], &buflen
[1]);
83 /* Check that the value appears like an integer encoded as
84 Simple-TLV. We don't check the tag because the tests cards I
85 have use 1 for both, the modulus and the exponent - the
86 example in the documentation gives 2 for the exponent. */
88 err
= gpg_error (GPG_ERR_TOO_SHORT
);
89 else if (buffer
[i
][1] != buflen
[i
]-2 )
90 err
= gpg_error (GPG_ERR_INV_OBJ
);
94 err
= gcry_sexp_build (&sexp
, NULL
,
95 "(public-key (rsa (n %b) (e %b)))",
96 (int)buflen
[0]-2, buffer
[0]+2,
97 (int)buflen
[1]-2, buffer
[1]+2);
104 if (!gcry_pk_get_keygrip (sexp
, grip
))
106 err
= gpg_error (GPG_ERR_INTERNAL
); /* i.e. RSA not supported by
111 for (i
=0; i
< 20; i
++)
112 sprintf (r_gripstr
+i
*2, "%02X", grip
[i
]);
114 gcry_sexp_release (sexp
);
121 do_learn_status (app_t app
, ctrl_t ctrl
)
124 char ct_buf
[100], id_buf
[100];
127 /* Output information about all useful objects. */
128 for (i
=0; filelist
[i
].fid
; i
++)
130 if (filelist
[i
].certtype
)
134 len
= app_help_read_length_of_cert (app
->slot
,
135 filelist
[i
].fid
, NULL
);
138 /* FIXME: We should store the length in the application's
139 context so that a following readcert does only need to
140 read that many bytes. */
141 sprintf (ct_buf
, "%d", filelist
[i
].certtype
);
142 sprintf (id_buf
, "NKS-DF01.%04X", filelist
[i
].fid
);
143 send_status_info (ctrl
, "CERTINFO",
144 ct_buf
, strlen (ct_buf
),
145 id_buf
, strlen (id_buf
),
149 else if (filelist
[i
].iskeypair
)
153 err
= keygripstr_from_pk_file (app
->slot
, filelist
[i
].fid
, gripstr
);
155 log_error ("can't get keygrip from FID 0x%04X: %s\n",
156 filelist
[i
].fid
, gpg_strerror (err
));
159 sprintf (id_buf
, "NKS-DF01.%04X", filelist
[i
].fid
);
160 send_status_info (ctrl
, "KEYPAIRINFO",
162 id_buf
, strlen (id_buf
),
174 /* Read the certificate with id CERTID (as returned by learn_status in
175 the CERTINFO status lines) and return it in the freshly allocated
176 buffer put into CERT and the length of the certificate put into
179 do_readcert (app_t app
, const char *certid
,
180 unsigned char **cert
, size_t *certlen
)
184 unsigned char *buffer
;
185 const unsigned char *p
;
187 int class, tag
, constructed
, ndef
;
188 size_t totobjlen
, objlen
, hdrlen
;
193 if (strncmp (certid
, "NKS-DF01.", 9) )
194 return gpg_error (GPG_ERR_INV_ID
);
196 if (!hexdigitp (certid
) || !hexdigitp (certid
+1)
197 || !hexdigitp (certid
+2) || !hexdigitp (certid
+3)
199 return gpg_error (GPG_ERR_INV_ID
);
200 fid
= xtoi_4 (certid
);
201 for (i
=0; filelist
[i
].fid
; i
++)
202 if ((filelist
[i
].certtype
|| filelist
[i
].iskeypair
)
203 && filelist
[i
].fid
== fid
)
205 if (!filelist
[i
].fid
)
206 return gpg_error (GPG_ERR_NOT_FOUND
);
208 /* If the requested objects is a plain public key, redirect it to
209 the corresponding certificate. The whole system is a bit messy
210 because we sometime use the key directly or let the caller
211 retrieve the key from the certificate. The rationale for
212 that is to support not-yet stored certificates. */
213 if (filelist
[i
].iskeypair
)
214 fid
= filelist
[i
].iskeypair
;
217 /* Read the entire file. fixme: This could be optimized by first
218 reading the header to figure out how long the certificate
220 err
= iso7816_select_file (app
->slot
, fid
, 0, NULL
, NULL
);
223 log_error ("error selecting FID 0x%04X: %s\n", fid
, gpg_strerror (err
));
227 err
= iso7816_read_binary (app
->slot
, 0, 0, &buffer
, &buflen
);
230 log_error ("error reading certificate from FID 0x%04X: %s\n",
231 fid
, gpg_strerror (err
));
235 if (!buflen
|| *buffer
== 0xff)
237 log_info ("no certificate contained in FID 0x%04X\n", fid
);
238 err
= gpg_error (GPG_ERR_NOT_FOUND
);
242 /* Now figure something out about the object. */
245 err
= parse_ber_header (&p
, &n
, &class, &tag
, &constructed
,
246 &ndef
, &objlen
, &hdrlen
);
249 if ( class == CLASS_UNIVERSAL
&& tag
== TAG_SEQUENCE
&& constructed
)
251 else if ( class == CLASS_UNIVERSAL
&& tag
== TAG_SET
&& constructed
)
254 return gpg_error (GPG_ERR_INV_OBJ
);
255 totobjlen
= objlen
+ hdrlen
;
256 assert (totobjlen
<= buflen
);
258 err
= parse_ber_header (&p
, &n
, &class, &tag
, &constructed
,
259 &ndef
, &objlen
, &hdrlen
);
265 else if (class == CLASS_UNIVERSAL
&& tag
== TAG_OBJECT_ID
&& !constructed
)
267 const unsigned char *save_p
;
269 /* The certificate seems to be contained in a userCertificate
270 container. Skip this and assume the following sequence is
274 err
= gpg_error (GPG_ERR_INV_OBJ
);
280 err
= parse_ber_header (&p
, &n
, &class, &tag
, &constructed
,
281 &ndef
, &objlen
, &hdrlen
);
284 if ( !(class == CLASS_UNIVERSAL
&& tag
== TAG_SEQUENCE
&& constructed
) )
285 return gpg_error (GPG_ERR_INV_OBJ
);
286 totobjlen
= objlen
+ hdrlen
;
287 assert (save_p
+ totobjlen
<= buffer
+ buflen
);
288 memmove (buffer
, save_p
, totobjlen
);
293 *certlen
= totobjlen
;
301 /* Verify the PIN if required. */
303 verify_pin (app_t app
,
304 gpg_error_t (*pincb
)(void*, const char *, char **),
307 iso7816_pininfo_t pininfo
;
310 /* Note that force_chv1 is never set but we do it here anyway so
311 that other applications may reuse this function. For example it
312 makes sense to set force_chv1 for German signature law cards.
313 NKS is very similar to the DINSIG draft standard. */
314 if ( app
->did_chv1
&& !app
->force_chv1
)
315 return 0; /* No need to verify it again. */
317 memset (&pininfo
, 0, sizeof pininfo
);
322 if (!opt
.disable_keypad
323 && !iso7816_check_keypad (app
->slot
, ISO7816_VERIFY
, &pininfo
) )
325 rc
= pincb (pincb_arg
,
326 _("||Please enter your PIN at the reader's keypad"),
330 log_info (_("PIN callback returned error: %s\n"),
335 /* Although it is possible to use a local PIN, we use the global
336 PIN for this application. */
337 rc
= iso7816_verify_kp (app
->slot
, 0, "", 0, &pininfo
);
338 /* Dismiss the prompt. */
339 pincb (pincb_arg
, NULL
, NULL
);
345 rc
= pincb (pincb_arg
, "PIN", &pinvalue
);
348 log_info ("PIN callback returned error: %s\n", gpg_strerror (rc
));
352 /* The following limits are due to TCOS but also defined in the
354 if (strlen (pinvalue
) < pininfo
.minlen
)
356 log_error ("PIN is too short; minimum length is %d\n",
359 return gpg_error (GPG_ERR_BAD_PIN
);
361 else if (strlen (pinvalue
) > pininfo
.maxlen
)
363 log_error ("PIN is too large; maximum length is %d\n",
366 return gpg_error (GPG_ERR_BAD_PIN
);
369 /* Although it is possible to use a local PIN, we use the global
370 PIN for this application. */
371 rc
= iso7816_verify (app
->slot
, 0, pinvalue
, strlen (pinvalue
));
377 if ( gpg_err_code (rc
) == GPG_ERR_USE_CONDITIONS
)
378 log_error (_("the NullPIN has not yet been changed\n"));
380 log_error ("verify PIN failed\n");
390 /* Create the signature and return the allocated result in OUTDATA.
391 If a PIN is required the PINCB will be used to ask for the PIN;
392 that callback should return the PIN in an allocated buffer and
393 store that in the 3rd argument. */
395 do_sign (app_t app
, const char *keyidstr
, int hashalgo
,
396 gpg_error_t (*pincb
)(void*, const char *, char **),
398 const void *indata
, size_t indatalen
,
399 unsigned char **outdata
, size_t *outdatalen
)
401 static unsigned char sha1_prefix
[15] = /* Object ID is 1.3.14.3.2.26 */
402 { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
403 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
404 static unsigned char rmd160_prefix
[15] = /* Object ID is 1.3.36.3.2.1 */
405 { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
406 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
409 unsigned char data
[35]; /* Must be large enough for a SHA-1 digest
410 + the largest OID _prefix above. */
412 if (!keyidstr
|| !*keyidstr
)
413 return gpg_error (GPG_ERR_INV_VALUE
);
414 if (indatalen
!= 20 && indatalen
!= 16 && indatalen
!= 35)
415 return gpg_error (GPG_ERR_INV_VALUE
);
417 /* Check that the provided ID is valid. This is not really needed
418 but we do it to enforce correct usage by the caller. */
419 if (strncmp (keyidstr
, "NKS-DF01.", 9) )
420 return gpg_error (GPG_ERR_INV_ID
);
422 if (!hexdigitp (keyidstr
) || !hexdigitp (keyidstr
+1)
423 || !hexdigitp (keyidstr
+2) || !hexdigitp (keyidstr
+3)
425 return gpg_error (GPG_ERR_INV_ID
);
426 fid
= xtoi_4 (keyidstr
);
427 for (i
=0; filelist
[i
].fid
; i
++)
428 if (filelist
[i
].iskeypair
&& filelist
[i
].fid
== fid
)
430 if (!filelist
[i
].fid
)
431 return gpg_error (GPG_ERR_NOT_FOUND
);
432 if (!filelist
[i
].issignkey
)
433 return gpg_error (GPG_ERR_INV_ID
);
435 /* Prepare the DER object from INDATA. */
438 /* Alright, the caller was so kind to send us an already
439 prepared DER object. Check that it is waht we want and that
440 it matches the hash algorithm. */
441 if (hashalgo
== GCRY_MD_SHA1
&& !memcmp (indata
, sha1_prefix
, 15))
443 else if (hashalgo
== GCRY_MD_RMD160
&& !memcmp (indata
, rmd160_prefix
,15))
446 return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM
);
447 memcpy (data
, indata
, indatalen
);
451 if (hashalgo
== GCRY_MD_SHA1
)
452 memcpy (data
, sha1_prefix
, 15);
453 else if (hashalgo
== GCRY_MD_RMD160
)
454 memcpy (data
, rmd160_prefix
, 15);
456 return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM
);
457 memcpy (data
+15, indata
, indatalen
);
460 rc
= verify_pin (app
, pincb
, pincb_arg
);
462 rc
= iso7816_compute_ds (app
->slot
, data
, 35, outdata
, outdatalen
);
469 /* Decrypt the data in INDATA and return the allocated result in OUTDATA.
470 If a PIN is required the PINCB will be used to ask for the PIN; it
471 should return the PIN in an allocated buffer and put it into PIN. */
473 do_decipher (app_t app
, const char *keyidstr
,
474 gpg_error_t (*pincb
)(void*, const char *, char **),
476 const void *indata
, size_t indatalen
,
477 unsigned char **outdata
, size_t *outdatalen
)
479 static const unsigned char mse_parm
[] = {
480 0x80, 1, 0x10, /* Select algorithm RSA. */
481 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
486 if (!keyidstr
|| !*keyidstr
|| !indatalen
)
487 return gpg_error (GPG_ERR_INV_VALUE
);
489 /* Check that the provided ID is valid. This is not really needed
490 but we do it to to enforce correct usage by the caller. */
491 if (strncmp (keyidstr
, "NKS-DF01.", 9) )
492 return gpg_error (GPG_ERR_INV_ID
);
494 if (!hexdigitp (keyidstr
) || !hexdigitp (keyidstr
+1)
495 || !hexdigitp (keyidstr
+2) || !hexdigitp (keyidstr
+3)
497 return gpg_error (GPG_ERR_INV_ID
);
498 fid
= xtoi_4 (keyidstr
);
499 for (i
=0; filelist
[i
].fid
; i
++)
500 if (filelist
[i
].iskeypair
&& filelist
[i
].fid
== fid
)
502 if (!filelist
[i
].fid
)
503 return gpg_error (GPG_ERR_NOT_FOUND
);
504 if (!filelist
[i
].isenckey
)
505 return gpg_error (GPG_ERR_INV_ID
);
507 /* Do the TCOS specific MSE. */
508 rc
= iso7816_manage_security_env (app
->slot
,
510 mse_parm
, sizeof mse_parm
);
512 rc
= verify_pin (app
, pincb
, pincb_arg
);
514 rc
= iso7816_decipher (app
->slot
, indata
, indatalen
, 0x81,
515 outdata
, outdatalen
);
520 /* Handle the PASSWD command. CHVNOSTR is currently ignored; we
521 always use VHV0. RESET_MODE is not yet implemented. */
523 do_change_pin (app_t app
, ctrl_t ctrl
, const char *chvnostr
,
525 gpg_error_t (*pincb
)(void*, const char *, char **),
533 if ((flags
& APP_CHANGE_FLAG_RESET
))
534 return gpg_error (GPG_ERR_NOT_IMPLEMENTED
);
536 if ((flags
& APP_CHANGE_FLAG_NULLPIN
))
538 /* With the nullpin flag, we do not verify the PIN - it would fail
539 if the Nullpin is still set. */
540 oldpin
= "\0\0\0\0\0";
545 err
= verify_pin (app
, pincb
, pincb_arg
);
552 /* TRANSLATORS: Do not translate the "|*|" prefixes but
553 keep it at the start of the string. We need this elsewhere
554 to get some infos on the string. */
555 err
= pincb (pincb_arg
, _("|N|New PIN"), &pinvalue
);
558 log_error (_("error getting new PIN: %s\n"), gpg_strerror (err
));
562 err
= iso7816_change_reference_data (app
->slot
, 0x00,
564 pinvalue
, strlen (pinvalue
));
570 /* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
572 do_check_pin (app_t app
, const char *keyidstr
,
573 gpg_error_t (*pincb
)(void*, const char *, char **),
576 return verify_pin (app
, pincb
, pincb_arg
);
580 /* Select the NKS 2.0 application. */
582 app_select_nks (app_t app
)
584 static char const aid
[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
585 int slot
= app
->slot
;
588 rc
= iso7816_select_application (slot
, aid
, sizeof aid
, 0);
591 app
->apptype
= "NKS";
593 app
->fnc
.learn_status
= do_learn_status
;
594 app
->fnc
.readcert
= do_readcert
;
595 app
->fnc
.getattr
= NULL
;
596 app
->fnc
.setattr
= NULL
;
597 app
->fnc
.genkey
= NULL
;
598 app
->fnc
.sign
= do_sign
;
599 app
->fnc
.auth
= NULL
;
600 app
->fnc
.decipher
= do_decipher
;
601 app
->fnc
.change_pin
= do_change_pin
;
602 app
->fnc
.check_pin
= do_check_pin
;