summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /scd
parentInitial commit. (diff)
downloadgnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz
gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog-20112592
-rw-r--r--scd/Makefile.am51
-rw-r--r--scd/Makefile.in846
-rw-r--r--scd/apdu.c3523
-rw-r--r--scd/apdu.h156
-rw-r--r--scd/app-common.h306
-rw-r--r--scd/app-dinsig.c574
-rw-r--r--scd/app-geldkarte.c408
-rw-r--r--scd/app-help.c285
-rw-r--r--scd/app-nks.c1428
-rw-r--r--scd/app-openpgp.c5480
-rw-r--r--scd/app-p15.c6290
-rw-r--r--scd/app-sc-hsm.c2087
-rw-r--r--scd/app.c1356
-rw-r--r--scd/atr.c290
-rw-r--r--scd/atr.h27
-rw-r--r--scd/ccid-driver.c4080
-rw-r--r--scd/ccid-driver.h158
-rw-r--r--scd/command.c2130
-rw-r--r--scd/iso7816.c1034
-rw-r--r--scd/iso7816.h154
-rw-r--r--scd/scdaemon-w32info.rc52
-rw-r--r--scd/scdaemon.c1454
-rw-r--r--scd/scdaemon.h145
-rw-r--r--scd/scdaemon.w32-manifest.in18
25 files changed, 34924 insertions, 0 deletions
diff --git a/scd/ChangeLog-2011 b/scd/ChangeLog-2011
new file mode 100644
index 0000000..9184af4
--- /dev/null
+++ b/scd/ChangeLog-2011
@@ -0,0 +1,2592 @@
+2011-12-01 Werner Koch <wk@g10code.com>
+
+ NB: ChangeLog files are no longer manually maintained. Starting
+ on December 1st, 2011 we put change information only in the GIT
+ commit log, and generate a top-level ChangeLog file from logs at
+ "make dist". See doc/HACKING for details.
+
+2011-12-01 Niibe Yutaka <gniibe@fsij.org>
+
+ * app-openpgp.c (do_change_pin): Fix pincb messages when
+ use_keypad == 1.
+
+2011-11-29 Niibe Yutaka <gniibe@fsij.org>
+
+ PC/SC pininput support for passphrase modification (2/2)
+ * apdu.h (apdu_send_simple_kp): Remove.
+
+ * apdu.c (pcsc_keypad_modify): Add bConfirmPIN handling.
+ (apdu_send_simple_kp): Remove.
+
+ * iso7816.h (iso7816_reset_retry_counter_kp): Remove arguments
+ of NEWCHV, and NEWCHVLEN.
+ (iso7816_reset_retry_counter_with_rc_kp, iso7816_put_data_kp): New.
+
+ * iso7816.c (iso7816_reset_retry_counter_with_rc_kp): New.
+ (iso7816_reset_retry_counter_kp): Call apdu_keypad_modify. Only
+ handle the case with PININFO.
+ (iso7816_reset_retry_counter): Don't call
+ iso7816_reset_retry_counter_kp.
+ (iso7816_put_data_kp): New.
+
+ * app-openpgp.c (do_change_pin): Add with_resetcode.
+ Handle keypad for unblocking pass phrase with resetcode,
+ setting up of resetcode, and unblocking by admin.
+
+ PC/SC pininput support for passphrase modification (1/2)
+ * iso7816.h (iso7816_change_reference_data_kp): Remove arguments
+ of OLDCHV, OLDCHVLEN, NEWCHV, and NEWCHVLEN.
+
+ * iso7816.c (iso7816_change_reference_data_kp): Call
+ apdu_keypad_modify.
+ (iso7816_change_reference_data): Don't call
+ iso7816_change_reference_data_kp.
+
+ * apdu.h (apdu_keypad_modify): New.
+
+ * apdu.c (pcsc_keypad_modify, apdu_keypad_modify): New.
+ (struct reader_table_s): New memeber function keypad_modify.
+ (new_reader_slot, open_ct_reader, open_ccid_reader)
+ (open_rapdu_reader): Initialize keypad_modify.
+
+ * app-openpgp.c (do_change_pin): Handle keypad and call
+ iso7816_change_reference_data_kp if it is the case.
+
+2011-11-28 Niibe Yutaka <gniibe@fsij.org>
+
+ * iso7816.h (iso7816_verify_kp): Remove arguments of CHV and CHVLEN.
+
+ * iso7816.c (iso7816_verify_kp): Call apdu_keypad_verify. Only
+ handle the case with PININFO.
+ (iso7816_verify): Call apdu_send_simple.
+
+ * app-openpgp.c (verify_a_chv, verify_chv3): Follow the change of
+ iso7816_verify_kp.
+
+ * app-nks.c (verify_pin): Likewise.
+
+ * app-dinsig.c (verify_pin): Likewise.
+
+ * apdu.c: Include "iso7816.h".
+ (struct reader_table_s): New memeber function keypad_verify.
+ Add fields verify_ioctl and modify_ioctl in pcsc.
+ (CM_IOCTL_GET_FEATURE_REQUEST, FEATURE_VERIFY_PIN_DIRECT)
+ (FEATURE_MODIFY_PIN_DIRECT): New.
+ (pcsc_control): New.
+ (control_pcsc_direct, control_pcsc_wrapped, control_pcsc)
+ (check_pcsc_keypad, pcsc_keypad_verify): New.
+ (ccid_keypad_verify, apdu_keypad_verify): New.
+ (new_reader_slot): Initialize with check_pcsc_keypad,
+ pcsc_keypad_verify, verify_ioctl and modify_ioctl.
+ (open_ct_reader): Initialize keypad_verify with NULL.
+ (open_ccid_reader): Initialize keypad_verify.
+ (open_rapdu_reader): Initialize keypad_verify with NULL.
+ (apdu_open_reader): Initialize pcsc_control.
+
+ * pcsc-wrapper.c (load_pcsc_driver): Initialize pcsc_control.
+ (handle_control): New.
+ (main): Handle the case 6 of handle_control.
+
+2011-08-10 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_killscd): Use the new assuan force close flag
+ if available.
+
+2011-08-08 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_decipher): Take care of accidentally passed
+ signed integer data with a leading 0.
+
+2011-06-16 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (send_key_data): Implemented chunked mode.
+ (change_keyattr): Increase limit to 4096.
+ (do_decipher): Adjust padding for 4096 bit keys.
+
+2011-02-23 Werner Koch <wk@g10code.com>
+
+ * apdu.c (apdu_open_reader): Lock in to CCID if used once.
+
+2011-01-25 NIIBE Yutaka <gniibe@fsij.org>,
+ Grant Olson <kgo@grant-olson.net> (wk)
+
+ * command.c (do_reset, get_reader_slot)
+ (update_reader_status_file): Fix handling of the VALID flag for
+ unplugged readers.
+
+2011-01-25 Werner Koch <wk@g10code.com>
+
+ From 2.0 branch, 2010-03-17:
+
+ * command.c (open_card): Return GPG_ERR_NOT_OPERATIONAL if no
+ card services are available.
+ (get_reader_slot): Detect no services status.
+ (cmd_serialno): No reset if there are no services.
+ (scd_command_handler): Stop scdaemon in that case.
+ * apdu.c (pcsc_no_service): New.
+ (open_pcsc_reader_direct): Set it.
+ (apdu_open_reader): Add arg R_NO_SERVICE.
+
+2011-01-05 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_transceive_secure): Support the gnuk token.
+
+2010-11-16 Werner Koch <wk@g10code.com>
+
+ * apdu.c (PCSC_UNKNOWN) [W32]: Fix all these values which don't
+ match those of libpcsc. Reported by Michael Petig.
+
+2010-10-27 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (create_socket_name): Use TMPDIR. Change callers.
+
+2010-10-18 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (parse_algorithm_attribute): Remove extra const in
+ definition of DESC.
+
+2010-08-16 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c: Replace remaining printf by es_printf.
+
+2010-06-09 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): s/log_set_get_tid_callback/log_set_pid_suffix_cb/.
+ (tid_log_callback): Adjust for this change.
+
+2010-03-11 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c: Include "asshelp.h".
+ (main): Remove assuan_set_assuan_log_prefix. Add
+ assuan_set_log_cb.
+ (handle_signal): Disable pth ctrl dumping.
+ * command.c (scd_command_handler): Remove assuan_set_log_stream.
+
+2010-03-10 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (scdaemon_LDADD): Remove libjnlib.a.
+
+2009-12-15 Werner Koch <wk@g10code.com>
+
+ * iso7816.c (do_generate_keypair): s/readonly/read_only/ because
+ the first is a keyword in VMS C.
+
+2009-12-03 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (set_debug): Allow for numerical debug leveles. Print
+ active debug flags.
+
+2009-11-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (scd_command_handler): Use assuan_fd_t and
+ assuan_fdopen on fds.
+
+2009-11-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (scd_command_handler): Call assuan_init_socket_server,
+ not assuan_init_socket_server_ext.
+
+2009-11-04 Werner Koch <wk@g10code.com>
+
+ * command.c (register_commands): Add help arg to
+ assuan_register_command. Add help strings to all commands.
+
+2009-11-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (reset_notify): Take LINE arg and return error.
+ (register_commands): Use assuan_handler_t type.
+
+2009-10-25 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (scd_deinit_default_ctrl): Release IN_DATA.
+ * command.c (cmd_setdata): Release IN_DATA. Reported by Klaus
+ Flittner.
+
+2009-10-16 Marcus Brinkmann <marcus@g10code.com>
+
+ * AM_CFLAGS, scdaemon_LDADD: Use libassuan instead of libassuan-pth.
+ * scdaemon.c: Invoke ASSUAN_SYSTEM_PTH_IMPL.
+ (main): Call assuan_set_system_hooks and assuan_sock_init.
+
+2009-09-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c: Include "scdaemon.h" before <assuan.h> because of
+ GPG_ERR_SOURCE_DEFAULT check.
+ (option_handler, open_card, cmd_serialno, cmd_lean, cmd_readcert)
+ (cmd_readkey, cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt)
+ (cmd_getattr, cmd_setattr, cmd_writecert, cmd_writekey)
+ (cmd_genkey, cmd_random, cmd_passwd, cmd_checkpin, cmd_lock)
+ (cmd_unlock, cmd_getinfo, cmd_restart, cmd_disconnect, cmd_apdu)
+ (cmd_killscd): Return gpg_error_t instead of int.
+ (scd_command_handler): Allocate assuan context before starting server.
+ * scdaemon.c (main): Update to new Assuan API.
+
+2009-09-03 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_decipher): Compute required Le.
+ * iso7816.c (iso7816_decipher): Add new arg LE.
+ * app-nks.c (do_decipher): Adjust for change.
+
+ * iso7816.c (iso7816_put_data, iso7816_put_data_odd): Turn DATA
+ into a void ptr.
+
+2009-08-05 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (change_keyattr_from_string): New.
+ (do_setattr): Support KEY-ATTR.
+
+2009-07-29 Marcus Brinkmann <marcus@g10code.com>
+
+ * ccid-driver.c (print_pr_data): Fix 64 bit compat problem.
+
+2009-07-24 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Enable hack for SCR 3320.
+
+2009-07-21 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c [HAVE_PTH]: Include pth.h.
+ (my_sleep): New.
+ (bulk_in): s/gnupg_sleep/my_sleep/.
+
+2009-07-20 Werner Koch <wk@g10code.com>
+
+ * apdu.c [GNUPG_MAJOR_VERSION==1]: Include dynload.h.
+
+2009-07-16 Werner Koch <wk@g10code.com>
+
+ * command.c (update_reader_status_file): Test for unplugged reader.
+ (TEST_CARD_REMOVAL): Ditto.
+ * app.c (select_application): Ditto.
+ * ccid-driver.c (bulk_out): Return CCID_DRIVER_ERR_NO_READER if a
+ reader was unplugged.
+ (struct ccid_driver_s): Turn nonnull_nad into an unsigned char.
+ Turn apdu_level, auto_ifsd, powered_off, has_pinpad into
+ bitfields. Add enodev_seen.
+ * apdu.c (apdu_prepare_exit): New.
+ (get_status_ccid): Return the status word and nut just -1.
+ * scdaemon.c (scd_exit): Call it.
+
+2009-07-13 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (struct ccid_driver_s): Add fields last_progress,
+ progress_cb and progress_cb_arg.
+ (ccid_set_progress_cb): New.
+ (print_progress): New.
+ (ccid_transceive): Call print_progress for wait time extensions.
+ * apdu.c (struct reader_table_s): Add field set_progress_cb.
+ (new_reader_slot): Clear that field.
+ (open_ccid_reader): Set it to ..
+ (set_progress_cb_ccid_reader): ... new fucntion.
+ * app.c (print_progress_line): New.
+ (lock_reader): Add arg CTRL to set a progress callback and
+ change all callers to provide it.
+ (unlock_reader): Remove the progress callback.
+
+2009-07-10 Werner Koch <wk@g10code.com>
+
+ * iso7816.c (iso7816_compute_ds): Add args EXTENDED_MODE and LE.
+ Change all callers to use 0.
+ (iso7816_internal_authenticate): Add args EXTENDED_MODE and LE.
+ * app-openpgp.c (do_sign): Take exmode and Le from card
+ capabilities and pass them to iso7816_compute_ds.
+ (do_auth): Ditto for iso7816_internal_authenticate.
+ (change_keyattr): Reset CHV verification status.
+
+2009-07-09 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (change_keyattr): New.
+ (do_writekey): Call it.
+
+ * app-openpgp.c (does_key_exist): Add arg GENERATING. Change
+ callers.
+
+2009-06-30 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_transceive): Set RESYNCING flag.
+
+2009-06-29 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_transceive): Add a hack to support extended
+ length for Omnikey readers.
+ (is_exlen_apdu): New.
+ (parse_ccid_descriptor): Track short+extended apdu exchange level.
+
+2009-06-18 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv2): Remove special case for v2 cards.
+ (get_public_key): Use extended mode.
+
+2009-06-17 Werner Koch <wk@g10code.com>
+
+ * iso7816.c (iso7816_get_data): Add arg EXTENDED_MODE. Change all
+ callers.
+ * app-openpgp.c (data_objects): Use bit flags. Add flag
+ TRY_EXTLENGTH.
+ (get_cached_data): Add arg TRY_EXTLEN and use it for iso7816_get_data.
+ (get_one_do): Use extended length APDU if necessary.
+
+2009-06-10 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (store_fpr): Change first arg to app_t; adjust
+ callers. Flush the cache.
+
+2009-06-09 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_readcert): Return NOT_FOUND if the retrieved
+ data has a length of zero.
+ (do_getattr): Add EXTCAP subkey "sm".
+
+2009-05-20 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv2): Add case for v2 cards.
+ (verify_chv3): Factor some code out to ..
+ (build_enter_admin_pin_prompt): .. new.
+ (do_change_pin): Properly handle v2 cards.
+
+2009-05-19 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (create_server_socket): Use SUN_LEN.
+ (JNLIB_NEED_AFLOCAL): Define.
+
+2009-05-13 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (abort_cmd): Add arg SEQNO and change callers.
+ (bulk_in): Retry on seqno mismatch.
+
+ * apdu.c (send_le): Release result_buffer.
+ (apdu_send_direct): Implemend extended length.
+ * command.c (cmd_apdu): Add option "--exlen".
+
+2009-05-11 Werner Koch <wk@g10code.com>
+
+ * apdu.c (send_le): Replace log_error by log_info.
+
+2009-05-08 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_genkey): Allow larger key sizes.
+ (do_decipher): Ditto.
+ * iso7816.c (do_generate_keypair): Add arg EXTENDED_MODE an LE.
+ (iso7816_generate_keypair, iso7816_read_public_key): Ditto.
+ Changed all callers.
+ * apdu.c (send_le): Implement extended length return values.
+
+ * ccid-driver.c (bulk_in): Retry on EAGAIN.
+ (abort_cmd): Change seqno handling.
+
+2009-04-28 Werner Koch <wk@g10code.com>
+
+ * app-help.c (app_help_count_bits): New.
+
+ * app-nks.c (switch_application): Detect mass signature cards.
+ Take care of new NEED_APP_SELECT flag.
+ (do_sign): Don't allow mass signature cards.
+ (all_zero_p): New.
+ (do_readkey): New.
+ (app_select_nks): Register do_readkey.
+
+2009-04-01 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_setattr, do_writekey): Prepare for extended
+ length cards.
+
+2009-03-31 Werner Koch <wk@g10code.com>
+
+ * command.c (percent_plus_unescape): Remove.
+ (cmd_setattr): Use percent_plus_unescape_inplace.
+
+2009-03-30 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (do_decipher): Make it work for TCOS 3.
+ * iso7816.c (iso7816_decipher): Add arg EXTENDED_MODE.
+ * apdu.c (apdu_send): Add arg EXTENDED_MODE and change all callers.
+ (apdu_send_le): Ditto.
+ (apdu_send_direct): Ditto, but not yet functional.
+ (send_le): Fix command chaining. Implement extended length option.
+ * ccid-driver.c (ccid_transceive): Remove restriction on apdu length.
+ (struct ccid_driver_s): Add field IFSC.
+ (ccid_get_atr): Set IFSC.
+ (ccid_transceive): Use negotiated IFSC and support S(IFS) command.
+
+2009-03-26 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_pksign): Allow more hash algorithms.
+
+ * scdaemon.h (MAX_DIGEST_LEN): Change to 64.
+
+ * apdu.c (open_ccid_reader): Clear the is_to flag.
+
+ * app-nks.c (filelist): Add field KID.
+ (do_getattr): Change standard authentication key.
+ (do_sign): Setup a security environment for TCOS 3 cards and support
+ all SHA-2 algorithms.
+
+2009-03-24 Werner Koch <wk@g10code.com>
+
+ * command.c (struct server_local_s): Add flag
+ APP_CTX_MARKED_FOR_RELEASE.
+ (do_reset): Set the flag.
+ (open_card): Act on this flag.
+ * app-common.h (struct app_ctx_s): Add flag NO_REUSE.
+ (application_notify_card_reset): Set the flag.
+ * app.c (select_application, release_application): Take care of
+ that flag.
+
+2009-03-20 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (keygripstr_from_pk_file): Fix for TCOS 3 cards.
+
+2009-03-18 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_pcsc_reader_wrapped): Use close_all_fds.
+
+ * command.c (cmd_learn): Add option --keypairinfo.
+ * app.c (app_write_learn_status): Add arg FLAGS.
+ * app-common.h (struct app_ctx_s): Add arg FLAGS to LEARN_STATUS.
+ Change all implementors.
+ * app-p15.c (do_learn_status): Take care of flag bit 0.
+ * app-nks.c (do_learn_status, do_learn_status_core): Ditto.
+
+2009-03-10 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (send_key_attr): New.
+ (do_getattr): New attribute KEY_ATTR.
+ * command.c (send_status_direct): New.
+
+2009-03-06 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (do_learn_status): Factor code out to..
+ (do_learn_status_core): .. new.
+ (do_readcert, do_sign, do_decipher): Switch to SigG if needed.
+ (verify_pin): Use DESC also for keypad based verify.
+
+2009-03-05 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_a_chv): Remove special case for keypads.
+ (verify_chv3): Ditto.
+
+ * app-nks.c (get_chv_status): New.
+ (parse_pwidstr): New.
+ (verify_pin): Add args PWID and DESC and use them. Remove the
+ CHV1 caching.
+ (do_change_pin): Allow PIN selection and add reset mode.
+ (do_learn_status): Use NKS-NKS3 tag for TCOS 3 cards.
+ (do_readcert, do_sign): Allow NKS-NKS3 tag.
+
+2009-03-04 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (do_getattr): New.
+ (app_select_nks): Register it.
+ (verify_pin): Factor some code out to...
+ (basic_pin_checks): New.
+ (do_change_pin): Call the basic check.
+ (app_select_nks): Move AID to ..
+ (aid_nks): .. new.
+ (aid_sigg): New.
+ (switch_application): New.
+ (do_getattr, do_learn_status, do_readcert, do_sign, do_decipher)
+ (do_change_pin, do_check_pin): Make sure we are in NKS mode.
+
+2009-03-03 Werner Koch <wk@g10code.com>
+
+ * command.c (scd_command_handler): Remove dereference of STOPME
+ after free.
+
+2009-02-27 Werner Koch <wk@g10code.com>
+
+ * app.c (get_supported_applications): New.
+ * command.c (cmd_getinfo): New subcommand "app_list"
+ (cmd_killscd): New.
+ (register_commands): Register command KILLSCD.
+ (struct server_local_s): Add field STOPME.
+ (scd_command_handler): Act upon this.
+
+2009-02-25 Werner Koch <wk@g10code.com>
+
+ * apdu.c (apdu_get_status): Factor all code out to ...
+ (apdu_private_get_status): .. new. Add arg NO_ATR_RESET.
+ (apdu_connect): Call new function.
+
+ * scdaemon.c: New option --debug-log-tid.
+ (tid_log_callback): New.
+ (main): Move debug-wait code after debug stream init.
+
+2009-02-24 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_get_atr): Move debug output to ..
+ (print_r2p_parameters): .. new.
+ (print_r2p_header, print_pr_data, print_r2p_unknown)
+ (print_r2p_datablock, print_r2p_slotstatus, print_r2p_escape)
+ (print_r2p_datarate): New.
+ (bulk_in): Call parameter printing.
+ (ccid_set_debug_level): Add debug level 3.
+ (convert_le_u16): New.
+ (print_p2r_header, print_p2r_iccpoweron, print_p2r_iccpoweroff)
+ (print_p2r_getslotstatus, print_p2r_xfrblock)
+ (print_p2r_getparameters, print_p2r_resetparameters)
+ (print_p2r_setparameters, print_p2r_escape, print_p2r_iccclock)
+ (print_p2r_to0apdu, print_p2r_secure, print_p2r_mechanical)
+ (print_p2r_abort, print_p2r_setdatarate, print_r2p_unknown): New.
+ (bulk_out): Add arg NO_DEBUG and change all callers to pass 0.
+ Call parameter printing.
+ (ccid_slot_status): Call with NO_DEBUG set.
+ (abort_cmd, send_escape_cmd, ccid_get_atr, ccid_get_atr)
+ (ccid_transceive_apdu_level, ccid_transceive)
+ (ccid_transceive_secure): Remove old debug print code.
+
+2009-02-12 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_getinfo): Add new subcommand "deny_admin".
+
+2009-01-28 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): Make --allow-admin the default and make the
+ option a dummy.
+
+2009-01-27 Werner Koch <wk@g10code.com>
+
+ * app-geldkarte.c: Changed to use an AID.
+
+ * app.c (app_munge_serialno): Add case for no serialno.
+ (app_get_serial_and_stamp): Ditto.
+
+2009-01-26 Werner Koch <wk@g10code.com>
+
+ * app-geldkarte.c: New.
+ * Makefile.am (card_apps): Add new file.
+ * app.c (select_application): Test for geldkarte.
+
+2009-01-12 Werner Koch <wk@g10code.com>
+
+ * command.c (send_client_notifications) [HAVE_W32_SYSTEM]: Fix
+ brackets.
+
+2009-01-08 Werner Koch <wk@g10code.com>
+
+ * iso7816.c (iso7816_read_record, iso7816_read_binary): Pass 0 for
+ L_e because the problem with the CCID driver has gone.
+ (iso7816_apdu_direct): New.
+
+ * app-nks.c (filelist): Add NKS_VER field. Add NKS 3 specific
+ entries.
+ (app_local_s, do_deinit): New.
+ (get_nks_version): New.
+ (app_select_nks): Setup local data.
+ (keygripstr_from_pk_file): Replace SLOT by APP and take care of
+ NKS version > 2.
+ (do_learn_status): Take care of NKS version.
+
+2009-01-05 Werner Koch <wk@g10code.com>
+
+ * apdu.c (apdu_get_status): Save the last status.
+
+2008-12-18 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (abort_cmd): New.
+ (bulk_in): Call abort_cmd after severe errors.
+
+ * apdu.c (reader_table_s): Add field ANY_STATUS.
+ (new_reader_slot): Clear it.
+ (apdu_get_status): Use ANY_STATUS to update the change counter.
+ Remove the use of the flag bit from LAST_STATUS everywhere.
+ * command.c (update_reader_status_file): Factor code out to ...
+ (send_client_notifications): New. Track signals already sent.
+ (update_reader_status_file): Shutdown the reader after a failed
+ apdu_get_status.
+
+2008-12-09 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): Call i18n_init before init_common_subsystems.
+
+2008-12-08 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (handle_connections): Sync ticker to the next full
+ interval.
+ (TIMERTICK_INTERVAL_USEC): Change to 500ms.
+
+2008-12-05 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (app_local_s): Add field ALGO_ATTR_CHANGE.
+ (app_select_openpgp): Parse new capability.
+ (show_caps): Show new capability.
+
+2008-12-03 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (opts): Use ARGPARSE_ macros. Add option
+ --card-timeout.
+ * command.c (update_reader_status_file): Implement it.
+
+2008-11-18 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (make_libversion): New.
+ (my_strusage): Print libgcrypt and libksba version.
+
+2008-11-03 Werner Koch <wk@g10code.com>
+
+ * command.c (server_local_s): Add field DISCONNECT_ALLOWED.
+ (cmd_disconnect): Implement command.
+ (open_card): Reset disconnect flag.
+ (update_reader_status_file): Disconnect if allowed.
+
+ * app-common.h (app_ctx_s): Remove INITIALIZED. Make REF_COUNT
+ unsigned.
+ * app.c (select_application): Remove INITIALIZED.
+ (app_write_learn_status, app_readcert, app_readkey, app_getattr)
+ (app_setattr, app_sign, app_decipher, app_writecert)
+ (app_writekey, app_get_challenge, app_change_pin, app_check_pin):
+ Replace INITIALIZED by REF_COUNT check.
+ (application_notify_card_removed): Rename to ..
+ (application_notify_card_reset): .. this. Change all callers.
+ * command.c (do_reset): Call application_notify_card_reset after
+ sending a reset.
+ (update_reader_status_file): Add arg SET_CARD_REMOVED.
+ (scd_update_reader_status_file): Pass true for new flag.
+ (do_reset): Pass false for new flag.
+
+ * app.c (app_get_serial_and_stamp): Use bin2hex.
+ * app-help.c (app_help_get_keygrip_string): Ditto.
+ * app-p15.c (send_certinfo, send_keypairinfo, do_getattr): Ditto.
+ * app-openpgp.c (send_fpr_if_not_null, send_key_data)
+ (retrieve_fpr_from_card, send_keypair_info): Ditto.
+ * app-nks.c (keygripstr_from_pk_file): Ditto.
+ * command.c (cmd_apdu): Ditto.
+
+2008-10-21 Marcus Brinkmann <marcus@g10code.com>
+
+ * command.c (open_card): If connect error is SW_HOST_NO_CARD,
+ return a more descriptive error.
+
+2008-10-20 Werner Koch <wk@g10code.com>
+
+ * pcsc-wrapper.c (read_32): Use provided arg and not stdin. Is
+ called with stdin, though.
+ (handle_close): Mark unused arg.
+ (handle_status, handle_reset): Ditto.
+
+ * ccid-driver.c (ccid_check_card_presence): Mark not yet used arg.
+
+ * scdaemon.c (scd_deinit_default_ctrl): Mark unused arg.
+ * command.c (cmd_unlock, cmd_restart, cmd_disconnect): Ditto.
+ * apdu.c (ct_get_status): Ditto.
+ (ct_send_apdu, pcsc_send_apdu_wrapped)
+ (apdu_open_remote_reader): Ditto.
+ * app.c (select_application): Ditto.
+ * app-openpgp.c (do_writecert, do_change_pin, do_writekey): Ditto.
+ * app-nks.c (do_change_pin, do_check_pin): Ditto.
+
+2008-10-16 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_disconnect): New dummy command.
+ (register_commands): Register command.
+
+2008-10-15 Werner Koch <wk@g10code.com>
+
+ * command.c (scd_command_handler): Return true if there is no more
+ active session.
+ * scdaemon.c (start_connection_thread): Set shutdown flag if
+ requested by command handler.
+ (main): Make PIPE_SERVER module global.
+ (handle_connections): Disable listen_fd if a shutdown is pending.
+
+2008-10-14 Werner Koch <wk@g10code.com>
+
+ * apdu.c (reader_table_s): Add fields connect_card and
+ disconnect_card.
+ (new_reader_slot): Set them to NULL.
+ (apdu_connect, apdu_disconnect): New.
+ (apdu_close_reader, apdu_shutdown_reader): Call apdu_disconnect.
+ (connect_pcsc_card, disconnect_pcsc_card): new.
+ (reset_pcsc_reader_direct): Implement in terms of
+ disconnect_pcsc_card and connect_pcsc_card.
+ (apdu_get_atr): Return NULL if there is no ATR.
+ * sc-copykeys.c (main): Add call to apdu_connect.
+ * command.c (open_card): Ditto.
+
+ * apdu.h (SW_HOST_ALREADY_CONNECTED): New.
+ (APDU_CARD_USABLE, APDU_CARD_PRESENT, APDU_CARD_ACTIVE): New.
+ * apdu.c: Replace constants by the new macros.
+ (open_pcsc_reader): Factor code out to ...
+ (open_pcsc_reader_direct, open_pcsc_reader_wrapped): New.
+ (reset_pcsc_reader): Factor code out to ...
+ (reset_pcsc_reader_direct, reset_pcsc_reader_wrapped): New.
+ (pcsc_get_status): Factor code out to ...
+ (pcsc_get_status_direct, pcsc_get_status_wrapped): New.
+ (pcsc_send_apdu): Factor code out to ...
+ (pcsc_send_apdu_direct, pcsc_send_apdu_wrapped): New.
+ (close_pcsc_reader): Factor code out to ...
+ (close_pcsc_reader_direct, close_pcsc_reader_wrapped): New.
+
+ * command.c (update_reader_status_file): Open the reader if not
+ yet done.
+
+ * scdaemon.c (TIMERTICK_INTERVAL_SEC, TIMERTICK_INTERVAL_USEC):
+ New to replace TIMERTICK_INTERVAL. Chnage from 2s (4 under W32)
+ to 250ms.
+
+2008-10-13 Werner Koch <wk@g10code.com>
+
+ * command.c (option_handler) [W32]: Use strtoul with base 16.
+ (update_reader_status_file) [W32]: Set Event.
+ (scd_command_handler): Use INT2FD to silent warning.
+
+2008-09-29 Werner Koch <wk@g10code.com>
+
+ * scdaemon.h (GCRY_MD_USER): Rename to GCRY_MODULE_ID_USER.
+ (GCRY_MD_USER_TLS_MD5SHA1): Rename to MD_USER_TLS_MD5SHA1 and
+ change all users.
+
+2008-09-28 Marcus Brinkmann <marcus@g10code.com>
+
+ * apdu.c (pcsc_get_status): Fix last change.
+
+2008-09-25 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_setattr): Do not allow setting of the reset
+ code.
+ (do_change_pin): Allow setting of the reset code.
+
+2008-09-24 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv3): Set the did_chv3 flag which was
+ accidently removed on 2008-03-26.
+ (verify_chv2): Revert last change.
+ (do_change_pin): Do not change CHV2. Add reset code logic for v2
+ cards.
+ * iso7816.c (iso7816_reset_retry_counter_with_rc): New.
+
+ * app-openpgp.c (add_tlv, build_privkey_template): New.
+ (do_writekey): Support v2 keys and other key lengths than 1024.
+ * iso7816.c (iso7816_put_data_odd): New.
+
+2008-09-23 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_sign): Support SHA-2 digests.
+ (verify_chv2): No CHV auto-sync for v2 cards.
+ (do_auth): Allow 2048 bit keys.
+ (parse_algorithm_attribute): New.
+ (rsa_key_format_t): New.
+ (struct app_local_s): Add struct KEYATTR.
+
+2008-09-23 Marcus Brinkmann <marcus@g10code.com>
+
+ * apdu.c (pcsc_get_status): Be more relaxed with the usable flag
+ under Windows.
+
+2008-09-23 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_setattr): Use command chaining for long
+ values.
+ * iso7816.c (iso7816_put_data): Add arg EXTENDED_MODE. Change all
+ callers.
+ * apdu.c (apdu_send_simple): Add arg EXTENDED_MODE. Change all
+ callers.
+ (send_le): Implement command chaining.
+ * ccid-driver.c (ccid_transceive_apdu_level): Increase allowed
+ APDU size.
+ (ccid_transceive): Alow for APDUS of up to 259 bytes.
+ * apdu.h: Add new SW_ codes.
+
+2008-09-16 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_writecert): New.
+ (register_commands): Register it.
+ * app-common.h (app_ctx_s): Add member WRITECERT.
+ * app.c (app_writecert): New.
+ * app-openpgp.c (do_writecert): New.
+ (parse_historical): New.
+ (show_extcap): New.
+ (dump_all_do): Print only the length of longs DOs.
+ * command.c (cmd_writekey, cmd_apdu, cmd_pksign)
+ (cmd_passwd): Replace open coding by skip_options.
+
+2008-08-30 Moritz <moritz@gnu.org>
+
+ * scdaemon.c (main): Use estream_asprintf instead of asprintf.
+ * command.c (update_reader_status_file): Likewise.
+ (cmd_serialno): Use estream_asprintf instead of asprintf
+ and xfree instead of free to release memory allocated
+ through (estream_)asprintf.
+ (cmd_learn): Likewise.
+ (pin_cb): Likewise.
+ * app-openpgp.c (get_public_key): Likewise.
+
+2008-08-18 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_setattr): Fix test for v2 cards.
+
+2008-08-11 Werner Koch <wk@g10code.com>
+
+ * apdu.c (reset_pcsc_reader, open_pcsc_reader)
+ (reset_rapdu_reader, open_rapdu_reader): Allow ATRs of up to 33
+ bytes. Provide maximum size of ATR buffer using DIM. Such long
+ ATR are never seen in reality but the PC/SC library of MAC OS X is
+ just too buggy. Reported by Ludovic Rousseau. Fixes bug #948.
+
+2008-07-30 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_a_chv): Use xtrymalloc and make the prompt
+ for CHV2 more user friendly.
+
+2008-07-03 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_readcert): New.
+ (app_local_s): Add fields IS_V2 and MAX_CERTLEN_3.
+ (app_select_openpgp): Set them and register do_readcert.
+ (do_setattr): Allow storing of the certificate.
+
+2008-06-25 Werner Koch <wk@g10code.com>
+
+ * app-dinsig.c (do_sign): Allow for SHA256.
+
+2008-06-24 Werner Koch <wk@g10code.com>
+
+ * app-common.h (app_ctx_s): Renamed reset_mode parameter of
+ change_pin to mode_Flags and make it an unsigned int.
+ (APP_CHANGE_FLAG_RESET, APP_CHANGE_FLAG_NULLPIN): New.
+ * app-openpgp.c (do_change_pin): Adjust for that.
+
+ * command.c (cmd_passwd): Add option --nullpin.
+ * app-nks.c (do_check_pin, do_change_pin): New.
+ (app_select_nks): Register new functions.
+
+2008-04-21 Moritz Schulte <mo@g10code.com> (wk)
+
+ * app-openpgp.c (verify_a_chv): Make use of the default CHV flag.
+
+2008-03-26 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv3): Support the keypad.
+
+2008-02-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * scdaemon.c (main): Use CONFIG_FILENAME as filename if it is set
+ in gpgconf-list output.
+
+2007-12-10 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_decipher): Take care of cryptograms shorter
+ that 128 bytes. Fixes bug#851.
+
+2007-11-14 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): Pass STANDARD_SOCKET flag to
+ create_server_socket.
+
+2007-11-13 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (start_connection_thread): Do not call
+ assuan_sock_check_nonce if we are running in --server mode.
+
+2007-11-07 Werner Koch <wk@g10code.com>
+
+ * scdaemon.h: Remove errors.h.
+
+2007-10-02 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_getinfo): Add "pid" subcommand.
+
+2007-10-01 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (create_server_socket): Use Assuan socket wrappers
+ and remove Windows specific code.
+ (socket_nonce): New.
+ (start_connection_thread): Check nonce.
+
+2007-09-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * scdaemon.c (main): New variable STANDARD_SOCKET, which is 1 for
+ W32 targets. Use it for create_socket_name.
+
+2007-08-07 Werner Koch <wk@g10code.com>
+
+ * tlv.c, tlv.h: Move to ../common/.
+
+2007-08-02 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c: Include gc-opt-flags.h and remove their definition
+ here.
+
+2007-08-01 Werner Koch <wk@g10code.com>
+
+ * apdu.c (send_le): Implement exact length hack. Suggested by
+ Sten Lindgren.
+
+2007-07-05 Werner Koch <wk@g10code.com>
+
+ * command.c (has_option_name, skip_options): New.
+ (cmd_genkey): Add option --timestamp.
+ (cmd_writekey): Enter confidential mode while inquiring the key data.
+
+ * app.c (app_genkey): Add arg CREATETIME.
+ * app-common.h (app_ctx_s): Likewise
+ * app-openpgp.c (do_genkey): Ditto. Use it.
+
+
+2007-07-04 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_getinfo): New subcommand "version".
+
+ * scdaemon.c (TIMERTICK_INTERVAL): New.
+ (handle_connections) [W32]: Enable a dummy sigs event.
+ (handle_connections): Use a proper count for select and not
+ FD_SETSIZE.
+ (fixed_gcry_pth_init, main): Kludge to fix pth initialization.
+
+2007-06-21 Werner Koch <wk@g10code.com>
+
+ * scdaemon.h (ctrl_t): Remove. It is now declared in ../common/util.h.
+
+2007-06-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * scdaemon.c (main): Percent escape output of --gpgconf-list.
+
+2007-06-12 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): Replace some calls by init_common_subsystems.
+
+2007-06-11 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (scdaemon_LDADD): Use libcommonpth macro.
+
+ * command.c (initialize_module_command): New.
+ * scdaemon.c (main) [W32]: Do not use sigpipe code.
+ (main): Call initialize_module_command.
+
+2007-06-06 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_sign): Fix arithmetic on void*.
+
+ * app.c (dump_mutex_state) [W32]: Handle the W32Pth case.
+
+ * apdu.c: Remove dynload.h.
+
+ * scdaemon.c (i18n_init): Remove.
+
+2007-04-20 Werner Koch <wk@g10code.com>
+
+ * sc-copykeys.c (my_gcry_logger): Removed.
+ (main): Call setup_libgcrypt_logging helper.
+ * scdaemon.c (my_gcry_logger): Removed.
+ (main): Call setup_libgcrypt_logging helper.
+
+2007-04-03 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_getinfo): New subcommand "reader_list".
+ * ccid-driver.c (scan_or_find_devices): Ignore EBUSY in scan mode
+ for special transports.
+
+2007-03-07 Werner Koch <wk@g10code.com>
+
+ * app-dinsig.c: Include i18n.h.
+ (verify_pin): Support PIN pads.
+ * app-nks.c (verify_pin): Ditto.
+
+ * ccid-driver.c (bulk_in): Handle time extension before checking
+ the message type.
+ (ccid_transceive_secure): Support the Cherry XX44 keyboard.
+ Kudos to the nice folks at Cherry for helping with that.
+
+2007-02-18 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (DEFAULT_PCSC_DRIVER): Add a default for OS X.
+
+2007-01-25 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (scdaemon_LDADD): Added LIBICONV. Noted by Billy
+ Halsey.
+
+2006-12-21 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv2): Factored most code out into...
+ (verify_a_chv): ... new.
+ (do_sign): Factored verification code out to new function and
+ take care of a keypad entered PIN.
+ (compare_fingerprint): Print an additional diagnostic.
+
+2006-11-28 Werner Koch <wk@g10code.com>
+
+ * apdu.c (send_le, apdu_send_direct): Increase RESULTLEN to 258 to
+ allow for full 256 byte and the status word. This might break
+ some old PC/SC drivers or cards, but we will see. Suggested by
+ Kenneth Wang.
+
+2006-11-23 Werner Koch <wk@g10code.com>
+
+ * command.c (scd_command_handler): Fixed use of CTRL.
+
+2006-11-21 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (libexec_PROGRAMS): Put pscs-wrapper into libexec.
+ Renamed to gnupg-pcsc-wrapper.
+ * apdu.c (open_pcsc_reader): Use GNUPG_LIBEXECDIR to accces the
+ wrapper. Suggested by Eric Dorland.
+
+2006-11-20 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv2): Support for keypads (only CHV2).
+
+ * ccid-driver.c (ccid_transceive_secure): Made it work for Kaan
+ and SCM.
+
+2006-11-17 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (scan_or_find_devices): Use DEBUGOUT_2 instead of
+ log_debug. Removed few other log_debug.
+
+ * iso7816.c (iso7816_check_keypad): Allow for a SW of 0.
+
+ * command.c (pin_cb): New mode to prompt for a keypad entry.
+
+ * scdaemon.c (main) <gpgconf-list>: Add disable-keypad.
+
+2006-11-15 Werner Koch <wk@g10code.com>
+
+ * app-p15.c (read_ef_odf): Cast one printf arg.
+
+ * scdaemon.h (struct server_control_s): Add field THREAD_STARTUP.
+ * command.c (scd_command_handler): Add new arg CTRL.
+ * scdaemon.c (scd_init_default_ctrl): Made static.
+ (scd_deinit_default_ctrl): New.
+ (start_connection_thread): Call init/deinit of ctrl.
+ (handle_connections): Allocate CTRL.
+
+ * apdu.c (PCSC_ERR_MASK): New.
+ (reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
+ (close_pcsc_reader, open_pcsc_reader): Use it after shifting error
+ values. Reported by Henrik Nordstrom. Fixes bug #724.
+
+2006-10-24 Werner Koch <wk@g10code.com>
+
+ * scdaemon.h (GCRY_MD_USER_TLS_MD5SHA1): New.
+ (MAX_DIGEST_LEN): Increased to 36.
+ * app-p15.c (do_sign): Support for TLS_MD5SHA1.
+ (do_auth): Detect TLS_MD5SHA1.
+ (do_sign): Tweaks for that digest.
+
+2006-10-23 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): New command --gpgconf-test.
+
+2006-10-17 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (scdaemon_LDADD): Link against libcommonpth.
+
+2006-10-12 Werner Koch <wk@g10code.com>
+
+ * apdu.c: Include pth.h after unistd.h for the sake of newer Pth
+ versions.
+
+2006-10-11 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_sign): Redirect to do_auth for OpenPGP.3.
+
+2006-10-06 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (AM_CFLAGS): Use PTH version of libassuan.
+ (scdaemon_LDADD): Ditto.
+
+ * scdaemon.h (send_status_info): Mark with sentinel attribute.
+
+2006-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (update_reader_status_file): Increase buffer of
+ NUMBUF2 (fixing typo).
+
+2006-09-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * app-openpgp.c (do_sign): Advance INDATA by the SHA1 resp. RMD160
+ prefix length.
+
+2006-09-14 Werner Koch <wk@g10code.com>
+
+ Replaced all call gpg_error_from_errno(errno) by
+ gpg_error_from_syserror().
+
+ * command.c (scd_command_handler): Replaced
+ init_connected_socket_server by init_socket_server_ext.
+
+2006-09-07 Werner Koch <wk@g10code.com>
+
+ * command.c (update_reader_status_file): Execute an event handler
+ if available.
+
+2006-09-06 Werner Koch <wk@g10code.com>
+
+ * apdu.c (pcsc_end_transaction):
+ * pcsc-wrapper.c (pcsc_end_transaction: Fixed dclaration.
+ Reported by Bob Dunlop.
+
+ * scdaemon.h (CTRL,APP): Removed and changed everywhere to
+ ctrl_t/app_t.
+
+ Replaced all Assuan error codes by libgpg-error codes. Removed
+ all map_to_assuan_status and map_assuan_err.
+
+ * scdaemon.c (main): Call assuan_set_assuan_err_source to have Assuan
+ switch to gpg-error codes.
+ * command.c (set_error): Adjusted.
+
+2006-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (get_reader_slot): Return the slot_table index, not
+ the APDU slot number.
+ (update_reader_status_file): Use the slot_table index in the
+ update_card_removed invocation.
+
+2006-09-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (cmd_getinfo): Handle status command.
+
+2006-08-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * command.c (do_reset): Delay resetting CTRL->reader_slot until
+ after update_card_removed invocation.
+
+2006-08-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * app-openpgp.c (do_decipher, do_sign): Allow "OPENPGP.2"
+ resp. "OPENPGP.1" for KEYIDSTR.
+
+2006-08-21 Werner Koch <wk@g10code.com>
+
+ * pcsc-wrapper.c (handle_open, handle_close): Reset card and
+ protocol on error/close.
+ (handle_status): Don't set the state if the state is unknown.
+ (handle_reset): Ignore an error if already disconnected. May
+ happen due to system wake-up after hibernation. Suggested by Bob
+ Dunlop.
+
+2006-06-28 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_writekey): Fixed computation of memmove
+ length. This led to garbled keys if E was larger than one byte.
+ Thanks to Achim Pietig for hinting at the garbled E.
+
+2006-06-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (scdaemon_LDADD): Add $(NETLIBS).
+
+2006-04-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * app.c (select_application): Cover up a slot mismatch error in
+ case it happens (it shouldn't happen).
+ (release_application): Use APP->slot. Lock the reader.
+ (application_notify_card_removed): Lock the reader.
+
+2006-04-11 Werner Koch <wk@g10code.com>
+
+ * command.c (hex_to_buffer): New.
+ (cmd_apdu): New.
+
+2006-04-03 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c [__GLIBC__]: Default to libpcsclite.so.1.
+
+2006-03-21 Werner Koch <wk@g10code.com>
+
+ * command.c (cmd_pksign): Add --hash option.
+
+2006-03-01 Werner Koch <wk@g10code.com>
+
+ * command.c (status_file_update_lock): New.
+ (scd_update_reader_status_file): Use lock and factor existing code
+ out to ..
+ (update_reader_status_file): .. this.
+ (do_reset): Use the lock and call update_reader_status_file.
+
+2006-02-20 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_pcsc_reader): Fixed double free. Thanks to Moritz.
+
+2006-02-09 Werner Koch <wk@g10code.com>
+
+ * command.c (get_reader_slot, do_reset)
+ (scd_update_reader_status_file): Rewrote.
+
+ * app.c (release_application): Factored code out to ..
+ (deallocate_app): new function.
+ (select_application): Introduce new saved application stuff.
+ (application_notify_card_removed): New.
+ * command.c (update_card_removed): Call it here.
+ (do_reset): And here.
+
+ * app.c (check_application_conflict): New.
+ * command.c (open_card): Use it here.
+ (cmd_restart): New command.
+
+ * command.c (cmd_lock): Fixed --wait option to actually terminate.
+
+2006-02-08 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_get_atr): Read Parameter and select T=1
+ using these parameters.
+ (scan_or_find_devices): Check for NULL r_fd.
+
+2006-02-02 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (special_transport): New
+ (ccid_open_reader, do_close_reader, ccid_shutdown_reader)
+ (bulk_out, bulk_in): Add support for CardMan 4040 reader.
+
+ * ccid-driver.c (scan_or_find_devices): Factored most code out to
+ (scan_or_find_usb_device): .. new.
+ (make_reader_id): Fixed vendor mask.
+
+2006-01-01 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_sign): Give user error if hash algorithm is
+ not supported by the card.
+
+2005-12-06 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_pcsc_reader): Check that pcsc-wrapper is actually
+ installed.
+
+2005-11-23 Werner Koch <wk@g10code.com>
+
+ * app-nks.c (verify_pin): Give a special error message for a Nullpin.
+
+2005-10-29 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (send_escape_cmd): New args RESULT, RESULTLEN and
+ RESULTMAX. Changed all callers.
+ (ccid_transceive_escape): New.
+
+2005-10-27 Werner Koch <wk@g10code.com>
+
+ * apdu.c [__CYGWIN__]: Make cygwin environment similar to _WIN32.
+ Suggested by John P. Clizbe.
+ * scdaemon.c [__CYGWIN__]: Set default PC/SC driver to winscard.dll.
+
+2005-10-19 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.h (CCID_DRIVER_ERR_NO_KEYPAD): New.
+ * apdu.h (SW_HOST_NO_KEYPAD): New.
+ * iso7816.h (struct iso7816_pininfo_s): New.
+ * iso7816.c (map_sw): Support new code.
+ (iso7816_check_keypad): New.
+ (iso7816_verify_kp, iso7816_change_reference_data_kp)
+ (iso7816_reset_retry_counter_kp): New. Extended versions of the
+ original functions.
+ * apdu.c (host_sw_string): Support new code.
+ (reader_table_s): New field CHECK_KEYPAD.
+ (new_reader_slot, open_ct_reader, open_pcsc_reader)
+ (open_ccid_reader, open_rapdu_reader): Initialize it.
+ (check_ccid_keypad): New.
+ (apdu_check_keypad): New.
+ (apdu_send_le): Factored all code out to ...
+ (send_le): .. new. Takes an additional arg; changed all callers
+ of the orginal function to use this one with a NULL for the new
+ arg.
+ (apdu_send_simple_kp): New.
+ (ct_send_apdu, pcsc_send_apdu, my_rapdu_send_apdu)
+ (send_apdu_ccid): New arg PININFO.
+ (send_apdu_ccid): Use the new arg.
+
+ * scdaemon.c: New option --disable-keypad.
+
+2005-10-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (scdaemon_LDADD): Add ../gl/libgnu.a after
+ ../common/libcommon.a.
+
+2005-09-20 Werner Koch <wk@g10code.com>
+
+ * app-dinsig.c (verify_pin): Try ISO 9564 BCD encoding.
+
+ * iso7816.c (iso7816_select_application): Add arg FLAGS. Changed
+ all callers to pass 0.
+ * app-openpgp.c (app_select_openpgp): But this one requires a
+ special flag.
+
+ * app-p15.c (app_select_p15): Don't use select application for the
+ BELPIC.
+
+2005-09-09 Werner Koch <wk@g10code.com>
+
+ * pcsc-wrapper.c (main): Removed bogus free.
+
+ * app-p15.c (do_auth): New.
+ (do_getattr): New attribs $AUTHKEYID and $DISPSERIALNO.
+ * app-openpgp.c (do_getattr): Ditto.
+
+2005-09-08 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_getattr): New key $AUTHKEYID.
+
+2005-09-06 Werner Koch <wk@g10code.com>
+
+ * app-p15.c (do_sign): Tweaked for BELPIC cards.
+ (read_home_df): New arg R_BELPIC.
+ (app_select_p15): Set card type for BELPIC.
+
+2005-09-05 Werner Koch <wk@g10code.com>
+
+ * iso7816.c (iso7816_select_path): New.
+ * app-p15.c (select_ef_by_path): Allow for direct path selection.
+ (app_select_p15): Try using the Belgian variant of pkcs#15.
+ (read_home_df): New.
+ (read_ef_odf): Generalized.
+ (read_ef_tokeninfo): New.
+ (read_p15_info): Set serialnumber from TokenInfo.
+ (app_select_p15): Don't munge serialNumber - that must be done
+ only once.
+
+ * iso7816.c (iso7816_read_binary): Use Le=0 when reading all
+ data. Handle 6C00 error and take 6B00 as indication for EOF.
+ * apdu.h (SW_EXACT_LENGTH_P): New.
+ * apdu.c (new_reader_slot, reset_pcsc_reader, pcsc_get_status)
+ (open_pcsc_reader): Set new reader state IS_T0.
+ (apdu_send_le): When doing T=0 make sure not to send Lc and Le.
+ Problem reported by Carl Meijer.
+ (apdu_send_direct): Initialize RESULTLEN.
+ * pcsc-wrapper.c (handle_status): Return the current protocol as
+ a new third word.
+
+2005-08-05 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_rapdu_reader): Set the reader number.
+
+2005-07-05 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_readkey): Return a mallcoed copy of the key as
+ required by the description. Thanks to Moritz for tracking this
+ problem down.
+
+2005-06-21 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): ifdef call to ccid_set_debug_level.
+
+ * apdu.c (reset_pcsc_reader, open_pcsc_reader): Cast size_t to
+ ulong for printf.
+
+2005-06-06 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): New option --debug-allow-core-dump.
+
+2005-06-03 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (handle_connections): Make sure that the signals we
+ are handling are not blocked.Block signals while creating new
+ threads.
+ (handle_connections): Include the file descriptor into the name of
+ the thread.
+
+2005-06-02 Werner Koch <wk@g10code.com>
+
+ * app.c (app_dump_state, dump_mutex_state): New.
+ * scdaemon.c (handle_signal): Print it on SIGUSR1.
+
+ * app-openpgp.c (do_writekey): Typo fix.
+
+ * command.c (open_card): Check for locked state even if an
+ application context is available.
+
+ * app-common.h: Add REF_COUNT field.
+ * app.c (release_application, select_application): Implement
+ reference counting to share the context beween connections.
+
+ * app.c (lock_reader, unlock_reader): Take SLOT instead of APP as
+ argument. Changed all callers.
+ (select_application): Unlock the reader on error. This should fix
+ the hangs I noticed last week.
+
+ * scdaemon.h: Removed card_ctx_t cruft.
+
+2005-06-01 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c: Include mkdtemp.h.
+
+2005-05-31 Werner Koch <wk@g10code.com>
+
+ * tlv.c [GNUPG_MAJOR_VERSION==1]: Define constants instead of
+ including a gnupg 1.4 header.
+
+2005-05-30 Werner Koch <wk@g10code.com>
+
+ * tlv.c: Add hack to compile without gpg-error.h when used with
+ GnuPG 1.4.
+
+2005-05-23 Werner Koch <wk@g10code.com>
+
+ * Makefile.am: Do not build sc-copykeys anymore.
+
+ * app-openpgp.c (app_openpgp_storekey, app_openpgp_readkey)
+ (app_openpgp_cardinfo): Removed.
+
+ * ccid-driver.c (parse_ccid_descriptor): SCR335 FW version 5.14 is
+ good.
+ (do_close_reader): Never do a reset. The caller should instead
+ make sure that the reader has been closed properly. The new retry
+ code in ccid_slot_status will make sure that the readersatrts up
+ fine even if the last process didn't closed the USB connection
+ properly.
+ (ccid_get_atr): For certain readers try switching to ISO mode.
+ Thanks to Ludovic Rousseau for this hint and the magic numbers.
+ (print_command_failed): New.
+ (bulk_in): Use it here. Add new arg NO_DEBUG.
+ (ccid_slot_status): Disabled debugging.
+
+2005-05-21 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (handle_signal): Print thread info on SIGUSR1.
+
+2005-05-20 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level.
+ (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good.
+ (ccid_transceive): Arghhh. The seqno is another bit in the
+ R-block than in the I block, this was wrong at one place.
+
+ * scdaemon.c: New options --debug-ccid-driver and
+ --debug-disable-ticker.
+
+ * app-openpgp.c (do_genkey, do_writekey): Factored code to check
+ for existing key out into ..
+ (does_key_exist): .. New function.
+
+2005-05-19 Werner Koch <wk@g10code.com>
+
+ * tlv.c (parse_sexp): New.
+
+ * command.c (cmd_writekey): New.
+ * app.c (app_writekey): New.
+ * app-common.c (app_t): Add function ptr WRITEKEY.
+ * app-openpgp.c (do_writekey): New.
+
+ * app-openpgp.c (do_readkey) [GNUPG_MAJOR_VERSION==1]: Return error.
+ * app-common.h (app_t) [GNUPG_MAJOR_VERSION==1]: Add a field to
+ store the Assuan context.
+
+2005-05-17 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c: Removed non-pth code paths.
+ (create_socket_name, create_server_socket): New. Taken from
+ ../agent/gpg-agent.
+ (cleanup): Changed to adjust for SOCKET_NAME now being malloced.
+ (ticker_thread): Always use pth_event_occurred; it is again
+ defined for all decent PTH versions.
+ (handle_connections): New. Based on the gpg-agent code.
+ (start_connection_thread): Ditto.
+ (ticker_thread): Removed.
+ (cleanup_sh): Removed.
+ (main): Run the handler for the pipe server in a separate
+ thread. This replaces the old ticker thread.
+ (scd_get_socket_name): New.
+ * command.c (cmd_getinfo): New command GETINFO.
+ (scd_command_handler): Renamed argument and changed code to use an
+ already connected FD.
+
+2005-05-15 Werner Koch <wk@g10code.com>
+
+ * app.c, app-common.h, app-nks.c, app-p15.c, app-dinsig.c
+ * app-openpgp.c: Change most function return types from int to
+ gpg_error_t.
+ * command.c (pin_cb): Ditto.
+ * sc-copykeys.c (pincb): Ditto.
+
+ * app.c (lock_reader, unlock_reader): New. Changed call handler
+ wrappers to make use of these functions.
+
+2005-05-07 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (do_close_reader): Don't do a reset before close.
+ Some folks reported that it makes the SCR335 hang less often.
+ Look at the source on how to re-enable it.
+
+2005-04-27 Werner Koch <wk@g10code.com>
+
+ * app-p15.c (micardo_mse): New.
+ (do_sign): Call it.
+ * iso7816.c (iso7816_manage_security_env): Allow passing DATA as
+ NULL to indicate an empty Lc.
+ * tlv.c (find_tlv): Check that a found object fits into the
+ buffer.
+ (find_tlv_unchecked): New as replacement for the old non-checking
+ variant.
+ * app.c (select_application): Keep on using the non-checking
+ variant.
+ * app-openpgp.c (get_one_do, dump_all_do): Ditto.
+
+
+ Removal of the old OpenSC based code.
+
+ * app-p15.c: New. Basic support for pkcs15 cards without OpenSC.
+ There are quite a couple of things missing but at least I can use
+ my old TCOS cards from the Aegypten-1 development for signing.
+ * app.c (select_application): Detect pkcs15 applications.
+ * Makefile.am (scdaemon_SOURCES): Removed card.c, card-common.h
+ and card-p15.c because they are now obsolete. Added app-p15.c.
+ Removed all OpenSC stuff.
+ * command.c (do_reset, open_card, cmd_serialno, cmd_learn)
+ (cmd_readcert, cmd_readkey, cmd_pksign, cmd_pkdecrypt): Removed
+ all special cases for the old card.c based mechanisms.
+ * scdaemon.c, apdu.c: Removed all special cases for OpenSC.
+
+2005-04-20 Werner Koch <wk@g10code.com>
+
+ * command.c: Use GPG_ERR_LOCKED instead of EBUSY.
+
+2005-04-14 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (retrieve_key_material): Rewritten. Return a
+ proper error code.
+ (retrieve_next_token): Removed.
+ (retrieve_fpr_from_card): Rewritten to make use of DO caching and
+ to take the KEYNO as arg.
+ (get_public_key): Renamed variable for clarity.
+
+2005-04-12 Werner Koch <wk@g10code.com>
+
+ Basic support for several sessions.
+
+ * command.c (scd_command_handler): Replace the primary_connection
+ stuff by a real connection list. Release the local context on
+ exit.
+ (scd_update_reader_status_file): Update accordingly. Send signal
+ to all connections who registered an event signal.
+ (cmd_lock, cmd_unlock, register_commands): New commands LOCK and
+ UNLOCK.
+ (cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt, cmd_setattr)
+ (cmd_genkey, cmd_passwd, cmd_checkpin): Return an error if reader
+ is locked.
+ (do_reset): Handle locking.
+ (open_card): Ditto. Share the reader slot with other sessions.
+ (get_reader_slot): New.
+ (update_card_removed): New. Use it in the TEST_CARD_REMOVAL macro.
+
+2005-04-07 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_check_pin): Add hack to allow verification of
+ CHV3.
+ (get_public_key): Don't use gcry functions to create S-expressions.
+ (do_deinit, do_readkey, do_genkey, send_keypair_info): Adjust for
+ above change.
+
+2005-03-29 Moritz Schulte <moritz@g10code.com>
+
+ * app-openpgp.c (retrieve_fpr_from_card): New function.
+ (retrieve_next_token): New function.
+ (retrieve_key_material): New function.
+ (get_public_key): Implement retrival of key through expernal
+ helper (gpg) in case the openpgp card is not cooperative enough.
+
+2005-03-16 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Make SCM workaround
+ reader type specific.
+ (scan_or_find_devices): Do not check the interface subclass in the
+ SPR532 kludge, as this depends on the firmware version.
+ (ccid_get_atr): Get the Slot status first. This solves the
+ problem with readers hanging on recent Linux 2.6.x.
+ (bulk_in): Add argument TIMEOUT and changed all callers to pass an
+ appropriate one. Change the standard timeout from 10 to 5 seconds.
+ (ccid_slot_status): Add a retry code with an initial short timeout.
+ (do_close_reader): Do an usb_reset before closing the reader.
+
+2005-02-25 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (get_public_key): Make sure not to return negative
+ numbers.
+ (do_sign): Allow passing of indata with algorithm prefix.
+ (do_auth): Allow OPENPGP.3 as an alternative ID.
+
+ * app.c (app_getattr): Return just the S/N but not the timestamp.
+
+2005-02-24 Werner Koch <wk@g10code.com>
+
+ * app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
+ application does dot support the getattr call.
+
+ * app-openpgp.c (get_one_do): Never try to get a non cacheable
+ object from the cache.
+ (get_one_do): Add new arg to return an error code. Changed all
+ callers.
+ (do_getattr): Let it return a proper error code.
+
+ * app.c (select_application): Return an error code and the
+ application context in an new arg.
+ * command.c (open_card): Adjusted for that. Don't use the
+ fallback if no card is present. Return an error if the card has
+ been removed without a reset.
+ (do_reset, cmd_serialno): Clear that error flag.
+ (TEST_CARD_REMOVAL): New. Use it with all command handlers.
+ (scd_update_reader_status_file): Set the error flag on all changes.
+
+ * scdaemon.c (ticker_thread): Termintate if a shutdown is pending.
+
+ * apdu.c: Added some PCSC error codes.
+ (pcsc_error_to_sw): New.
+ (reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
+ (open_pcsc_reader): Do proper error code mapping.
+
+2005-03-16 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Make SCM workaround
+ reader type specific.
+ (scan_or_find_devices): Do not check the interface subclass in the
+ SPR532 kludge, as this depends on the firmware version.
+ (ccid_get_atr): Get the Slot status first. This solves the
+ problem with readers hanging on recent Linux 2.6.x.
+
+2005-02-22 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (app_local_s): New field PK.
+ (do_deinit, do_genkey, app_openpgp_storekey): Clear it.
+ (get_public_key, send_keypair_info): New.
+ (do_learn_status): Send KEYPAIR info
+
+ * app-common.h (app_ctx_t): Add function pointer READKEY.
+ * app.c (app_readkey): New.
+ * command.c (cmd_readkey): Use READKEY function if possible.
+
+2005-01-26 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Need the CSM workaround
+ also for newer firmware versions. Need to get a list of fixed
+ firmware versions and use that.
+
+2005-01-25 Werner Koch <wk@g10code.com>
+
+ * apdu.c (apdu_send_le, apdu_send_direct): Fix some compiler
+ warnings.
+
+ * app-openpgp.c (get_cached_data): New arg GET_IMMEDIATE to bypass
+ the cache. Changed all callers.
+ (get_one_do): Bypass the cache if the value would have been read
+ directly for v1.1 cards.It makes things a bit slower but obnly for
+ 1.0 cards and there are not that many cards out in the wild. This
+ is required to fix a caching bug when generating new keys; as a
+ side effect of the retrieval of the the C4 DO from the 6E DO the
+ cached fingerprint will get updated to the old value and later
+ when signing the generated key the checking of the fingerprint
+ fails because it won't match the new one. Thanks to Moritz for
+ analyzing this problem.
+ (verify_chv3): Removed the CHV status reread logic because we
+ won't cache the C4 DO anymore.
+
+2004-12-28 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (find_endpoint): New.
+ (scan_or_find_devices): Add new args to return endpoint info and
+ interface number.
+ (ccid_open_reader, ccid_shutdown_reader): Take care of these new
+ args.
+ (bulk_in, bulk_out): Use the correct endpoints.
+ (ccid_transceive_apdu_level): New.
+ (ccid_transceive): Divert to above.
+ (parse_ccid_descriptor): Allow APDU level exchange mode.
+ (do_close_reader): Pass the interface number to usb_release_interface.
+
+2004-12-21 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main): Use default_homedir().
+
+2004-12-18 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c (main) [W32]: Remove special Pth initialize..
+
+ * scdaemon.h (map_assuan_err): Define in terms of
+ map_assuan_err_with_source.
+
+2004-12-15 Werner Koch <wk@g10code.com>
+
+ * scdaemon.c [W32]: Various hacks to make it run under W32.
+
+ * command.c (scd_update_reader_status_file) [W32]: Don't use kill.
+
+ * apdu.c [W32]: Disable use of pcsc_wrapper.
+
+ * Makefile.am (scdaemon_LDADD): Reorder libs.
+ (sc_copykeys_LDADD): Add libassuan because it is needed for W32.
+
+2004-12-06 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (pkglib_PROGRAMS): Build only for W32.
+
+2004-10-22 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv3): The minium length for CHV3 is
+ 8. Changed string to match the other ones.
+
+2004-10-21 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_sign): Replace asprintf by direct allocation.
+ This avoids problems with missing vasprintf implementations in
+ gnupg 1.4.
+
+ * app-common.h (app_openpgp_storekey: Add prototype.
+
+2004-10-20 Werner Koch <wk@g10code.com>
+
+ * sc-investigate: Removed.
+ * Makefile.am (sc_investigate): Removed.
+
+ * pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func.
+ (handle_open): Succeed even without a present card.
+ (handle_status, handle_reset): New.
+
+ * apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion.
+ (pcsc_get_status): Implemented.
+ (reset_pcsc_reader): Implemented.
+ (open_pcsc_reader): Succeed even with no card inserted.
+ (open_ccid_reader): Set LAST_STATUS.
+
+ * iso7816.c (iso7816_select_application): Always use 0 for P1.
+
+2004-10-18 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_get_atr): Reset T=1 state info.
+
+2004-10-14 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (parse_login_data): New.
+ (app_select_openpgp): Call it.
+ (do_setattr): Reparse it after change.
+
+2004-10-06 Werner Koch <wk@g10code.de>
+
+ * ccid-driver.c (ccid_open_reader): Store the vendor ID.
+ (ccid_transceive_secure): New.
+ (parse_ccid_descriptor): Workaround for an SCM reader problem.
+
+2004-10-04 Werner Koch <wk@g10code.de>
+
+ * ccid-driver.c (send_escape_cmd): New.
+
+2004-09-30 Werner Koch <wk@g10code.com>
+
+ * Makefile.am: Adjusted for gettext 0.14.
+
+ * app-openpgp.c (do_sign): Add the error string to the verify
+ failed messages.
+
+2004-09-27 Werner Koch <wk@g10code.com>
+
+ From gnupg 1.3
+
+ * app-openpgp.c: Made all strings translatable.
+ (verify_chv3) [GNUPG_MAJOR_VERSION]: Make opt.allow_admin
+ available for use in gnupg 2.
+ (verify_chv3): Reimplemented countdown showing to use only
+ functions from this module. Flush the CVH status cache on a
+ successful read.
+ (get_one_do): Hack to bypass the cache for cards versions > 1.0.
+ (store_fpr): Store the creation date for card version > 1.0.
+
+ * app-openpgp.c (app_openpgp_storekey): Call flush_cache.
+ (get_cached_data): Move local data initialization to ..
+ (app_select_openpgp): .. here. Read some flags for later use.
+ (do_getattr): New read-only attribute EXTCAP.
+
+ * apdu.c (open_pcsc_reader): Do not print empty reader string.
+
+ * ccid-driver.c (do_close_reader): Factored some code out from ...
+ (ccid_close_reader): ..here.
+ (ccid_shutdown_reader): New.
+
+ * apdu.c (apdu_shutdown_reader): New.
+ (shutdown_ccid_reader): New.
+
+ * apdu.c (open_ccid_reader): New arg PORTSTR. Pass it to
+ ccid_open_reader.
+ (apdu_open_reader): Pass portstr to open_ccid_reader.
+ (apdu_open_reader): No fallback if a full CCID reader id has been
+ given.
+
+ * ccid-driver.c (ccid_get_reader_list): New.
+ (ccid_open_reader): Changed API to take a string for the reader.
+ Removed al the cruft for the libusb development vesion which seems
+ not to be maintained anymore and there are no packages anyway.
+ The stable library works just fine.
+ (struct ccid_reader_id_s): Deleted and replaced everywhere by a
+ simple string.
+ (usb_get_string_simple): Removed.
+ (bulk_in): Do valgrind hack here and not just everywhere.
+
+ * ccid-driver.c (read_device_info): Removed.
+ (make_reader_id, scan_or_find_devices): New.
+ (ccid_open_reader): Simplified by make use of the new functions.
+ (ccid_set_debug_level): New. Changed the macros to make use of
+ it. It has turned out that it is often useful to enable debugging
+ at runtime so I added this option.
+
+ From gnupg 1.3 - David Shaw <dshaw@jabberwocky.com>
+
+ * app-openpgp.c (verify_chv3): Show a countdown of how many wrong
+ admin PINs can be entered before the card is locked.
+
+ * app-openpgp.c (get_cached_data): Avoid mallocing zero since it
+ breaks us when using --enable-m-guard.
+
+ * ccid-driver.c (usb_get_string_simple): Replacement function to
+ work with older libusb.
+
+ * ccid-driver.c (read_device_info): Fix segfault when usb device
+ is not accessible.
+ (ccid_open_reader): Allow working with an even older version of
+ libusb (usb_busses global instead of usb_get_busses()).
+
+2004-09-11 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (app_select_openpgp): Its app_munge_serialno and
+ not app_number_serialno.
+
+2004-08-20 Werner Koch <wk@g10code.de>
+
+ * app.c (select_application): Fixed serial number extraction and
+ added the BMI card workaround.
+ (app_munge_serialno): New.
+ * app-openpgp.c (app_select_openpgp): Try munging serialno.
+
+2004-08-05 Werner Koch <wk@g10code.de>
+
+ * scdaemon.c (main): New option --disable-application.
+ * app.c (is_app_allowed): New.
+ (select_application): Use it to check for disabled applications.
+
+ * ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New.
+ * ccid-driver.c (ccid_open_reader): Support the stable 0.1 version
+ of libusb.
+ (ccid_get_atr): Handle short messages.
+
+ * apdu.c (my_rapdu_get_status): Implemented.
+
+2004-07-27 Moritz Schulte <moritz@g10code.com>
+
+ * apdu.c: Include <signal.h>.
+
+ * Makefile.am: Use @DL_LIBS@ instead of -ldl.
+
+2004-07-22 Werner Koch <wk@g10code.de>
+
+ * Makefile.am: Make OpenSC lib link after libgcrypt. Do not link
+ to pth.
+ * apdu.c: Don't use Pth if we use OpenSC.
+ * sc-investigate.c, scdaemon.c: Disable use of pth if OpenSC is used.
+
+ * scdaemon.c (main): Bumbed thread stack size up to 512k.
+
+2004-07-16 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (reader_table_s): Add function pointers for the backends.
+ (apdu_close_reader, apdu_get_status, apdu_activate)
+ (send_apdu): Make use of them.
+ (new_reader_slot): Intialize them to NULL.
+ (dump_ccid_reader_status, ct_dump_reader_status): New.
+ (dump_pcsc_reader_status): New.
+ (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+ (open_osc_reader, open_rapdu_reader): Intialize function pointers.
+ (ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu)
+ (error_string): Removed. Replaced by apdu_strerror.
+ (get_ccid_error_string): Removed.
+ (ct_activate_card): Remove the unused loop.
+ (reset_ct_reader): Implemented.
+ (ct_send_apdu): Activate the card if not yet done.
+ (pcsc_send_apdu): Ditto.
+
+2004-07-15 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.h: Add error codes.
+ * ccid-driver.c: Implement more or less proper error codes all
+ over the place.
+
+ * apdu.c (apdu_send_direct): New.
+ (get_ccid_error_string): Add some error code mappings.
+ (send_apdu): Pass error codes along for drivers already supporting
+ them.
+ (host_sw_string): New.
+ (get_ccid_error_string): Use above.
+ (send_apdu_ccid): Reset the reader if it has not yet been done.
+ (open_ccid_reader): Don't care if the ATR can't be read.
+ (apdu_activate_card): New.
+ (apdu_strerror): New.
+ (dump_reader_status): Only enable it with opt.VERBOSE.
+ * iso7816.c (map_sw): Add mappings for the new error codes.
+
+2004-07-02 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+ (reset_ccid_reader, open_osc_reader): Call dump_reader_status only
+ in verbose mode.
+
+2004-07-01 Werner Koch <wk@gnupg.org>
+
+ * sc-investigate.c: Initialize Pth which is now required.
+ (interactive_shell): New command "readpk".
+
+ * app-openpgp.c (do_getattr): Fix for sending CA-FPR.
+
+2004-06-30 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (app_openpgp_readkey): Fixed check for valid
+ exponent.
+
+2004-06-18 Werner Koch <wk@g10code.com>
+
+ * sc-investigate.c (my_read_line): Renamed from read_line.
+
+2004-06-16 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (osc_get_status): Fixed type in function name. Noted by
+ Axel Thimm. Yes, I didn't tested it with OpenSC :-(.
+
+2004-04-28 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (do_setattr): Sync FORCE_CHV1.
+
+2004-04-27 Werner Koch <wk@gnupg.org>
+
+ * app-common.h: Do not include ksba.h for gnupg 1.
+
+2004-04-26 Werner Koch <wk@gnupg.org>
+
+ * app-common.h: New members FNC.DEINIT and APP_LOCAL.
+ * app.c (release_application): Call new deconstructor.
+ * app-openpgp.c (do_deinit): New.
+ (get_cached_data, flush_cache_item, flush_cache_after_error)
+ (flush_cache): New.
+ (get_one_do): Replaced arg SLOT by APP. Make used of cached data.
+ (verify_chv2, verify_chv3): Flush some cache item after error.
+ (do_change_pin): Ditto.
+ (do_sign): Ditto.
+ (do_setattr): Flush cache item.
+ (do_genkey): Flush the entire cache.
+ (compare_fingerprint): Use cached data.
+
+ * scdaemon.c (main): Do the last change the usual way. This is so
+ that we can easily test for versioned config files above.
+
+2004-04-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * scdaemon.c (main): For now, always print default filename for
+ --gpgconf-list, and never /dev/null.
+
+2004-04-21 Werner Koch <wk@gnupg.org>
+
+ * command.c (scd_update_reader_status_file): Send a signal back to
+ the client.
+ (option_handler): Parse the new event-signal option.
+
+ * scdaemon.c (handle_signal): Do not use SIGUSR{1,2} anymore for
+ changing the verbosity.
+
+2004-04-20 Werner Koch <wk@gnupg.org>
+
+ * command.c (scd_update_reader_status_file): Write status files.
+
+ * app-help.c (app_help_read_length_of_cert): Fixed calculation of
+ R_CERTOFF.
+
+ * pcsc-wrapper.c: New.
+ * Makefile.am (pkglib_PROGRAMS): Install it here.
+ * apdu.c (writen, readn): New.
+ (open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the
+ pcsc-wrapper if we are using Pth.
+ (apdu_send_le): Reinitialize RESULTLEN. Handle SW_EOF_REACHED
+ like SW_SUCCESS.
+
+2004-04-19 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (parse_ccid_descriptor): Store some of the reader
+ features away. New arg HANDLE
+ (read_device_info): New arg HANDLE. Changed caller.
+ (bulk_in): Handle time extension requests.
+ (ccid_get_atr): Setup parameters and the IFSD.
+ (compute_edc): New. Factored out code.
+ (ccid_transceive): Use default NADs when required.
+
+2004-04-14 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.h (server_control_s): Add member READER_SLOT.
+ * scdaemon.c (scd_init_default_ctrl): Initialize READER_SLOT to -1.
+ * command.c (open_card): Reuse an open slot.
+ (reset_notify): Just reset the slot if supported by the reader.
+ (do_reset): Factored code from above out.
+ (scd_command_handler): Use it for cleanup.
+
+ * apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
+ SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
+ * iso7816.c (map_sw): Map it.
+
+ * ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
+ * apdu.c (apdu_get_status): New.
+ (ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
+ (get_status_ccid): New.
+ (apdu_reset): New.
+ (reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
+ (reset_ccid_reader): New.
+ (apdu_enum_reader): New.
+
+ * apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
+ (new_reader_slot) [USE_GNU_PTH]: Init mutex.
+ (apdu_reset, apdu_get_status, apdu_send_le): Run functions
+ in locked mode.
+
+ * command.c (scd_update_reader_status_file): New.
+ * scdaemon.c (handle_tick): Call it.
+
+2004-04-13 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c: Convert to a Pth application.
+ (handle_signal, ticker_thread, handle_tick): New.
+ (main): Fire up the ticker thread in server mode.
+
+2004-03-23 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.
+
+2004-03-17 Werner Koch <wk@gnupg.org>
+
+ * tlv.c (parse_ber_header): Do not check for tag overflow - it
+ does not make sense. Simplified the check for length overflow.
+
+ * scdaemon.c (main) <gpgconf>: Fixed default value quoting.
+
+2004-03-16 Werner Koch <wk@gnupg.org>
+
+ * app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c
+ * app-nks.c (get_length_of_cert): Removed.
+ * app-help.c: New.
+ (app_help_read_length_of_cert): New. Code taken from above. New
+ optional arg R_CERTOFF.
+
+ * card-dinsig.c: Removed.
+ * card.c (card_get_serial_and_stamp): Do not bind to the old and
+ never finsiged card-dinsig.c.
+
+ * iso7816.c (iso7816_read_binary): Allow for an NMAX > 254.
+
+2004-03-11 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.h (out_of_core): Removed. Replaced callers by standard
+ gpg_error function.
+
+ * apdu.c, iso7816.c, ccid-driver.c [GNUPG_SCD_MAIN_HEADER]: Allow
+ to include a header defined by the compiler. This helps us to
+ reuse the source in other software.
+
+2004-03-10 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_read_record): New arg SHORT_EF. Changed all
+ callers.
+
+2004-02-18 Werner Koch <wk@gnupg.org>
+
+ * sc-investigate.c (main): Setup the used character set.
+ * scdaemon.c (main): Ditto.
+
+ * scdaemon.c (set_debug): New. Add option --debug-level.
+ (main): Add option --gpgconf-list.
+
+2004-02-12 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Include cmacros.am for common flags.
+
+2004-01-29 Werner Koch <wk@gnupg.org>
+
+ * command.c (reset_notify): Release the application context and
+ close the reader.
+
+2004-01-28 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_manage_security_env): New.
+ (iso7816_decipher): Add PADIND argument.
+
+2004-01-27 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
+
+ * app-common.h (app_ctx_s): Added readcert field.
+ * app.c (app_readcert): New.
+ * tlv.c (parse_ber_header): Added; taken from libksba.
+
+2004-01-26 Werner Koch <wk@gnupg.org>
+
+ * card.c (map_sc_err): Use SCD as the error source.
+
+ * command.c (open_card): ADD arg NAME to allow requesting a
+ specific application. Changed all callers.
+ (cmd_serialno): Allow optional argument to select the desired
+ application.
+
+ * app-nks.c: New.
+
+ * scdaemon.h (opt): Add READER_PORT.
+ * scdaemon.c (main): Set it here.
+ * app.c (app_set_default_reader_port): Removed.
+ (select_application): Add NAME arg and figure out a
+ default serial number from the GDO. Add SLOT arg and remove all
+ reader management.
+ (release_application): New.
+ (app_write_learn_status): Output an APPTYPE status line.
+ * command.c (open_card): Adapt for select_application change.
+ * app-openpgp.c (app_select_openpgp): Removed SN and SNLEN args
+ and set it directly. Changed all callers.
+
+2004-01-25 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_select_application): P1 kludge for OpenPGP
+ card.
+ * app-openpgp.c (find_tlv): Factor out this function to ..
+ * tlv.c, tlv.h: .. new.
+
+ * scdaemon.h: Introduced app_t and ctrl_t as the new types for APP
+ and CTRL.
+
+2004-01-21 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (apdu_send_le): Treat SW_EOF_REACHED as a warning.
+
+2004-01-20 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_read_binary): New.
+ (iso7816_select_file): New.
+ (iso7816_list_directory): New.
+
+ * sc-investigate.c: Add option -i.
+ (select_app, read_line, interactive_shell): New.
+
+2004-01-16 Werner Koch <wk@gnupg.org>
+
+ * apdu.h: Add SW_FILE_NOT_FOUND.
+ * iso7816.c (map_sw): Map it to GPG_ERR_ENOENT.
+ * iso7816.c (iso7816_select_file): New.
+
+ * app-dinsig.c: New file w/o any real code yet.
+ * Makefile.am (scdaemon_SOURCES,sc_investigate_SOURCES): Add file.
+
+ * sc-investigate.c: Add option --disable-ccid.
+
+2003-12-19 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (apdu_send_le): Send a get_response with the indicated
+ length and not the 64 bytes we used for testing.
+
+ * app-openpgp.c (verify_chv2, verify_chv3, do_sign): Check the
+ minimum length of the passphrase, so that we don't need to
+ decrement the retry counter.
+
+2003-12-17 Werner Koch <wk@gnupg.org>
+
+ * card-p15.c (p15_enum_keypairs): Replaced KRC by RC.
+ * card-dinsig.c (dinsig_enum_keypairs): Ditto.
+
+2003-12-16 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c (main): Set the prefixes for assuan logging.
+
+2003-11-17 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c, scdaemon.h: New options --allow-admin and --deny-admin.
+ * app-openpgp.c (verify_chv3): Check it here.
+
+2003-11-12 Werner Koch <wk@gnupg.org>
+
+ Adjusted for API changes in Libksba.
+
+2003-10-30 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (close_ct_reader, close_pcsc_reader): Implemented.
+ (get_ccid_error_string): New. Not very useful messages, though.
+
+2003-10-25 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (ccid_open_reader): Return an error if no USB
+ devices are found.
+
+ * command.c (cmd_genkey, cmd_passwd): Fixed faulty use of
+ !spacep().
+
+ * apdu.c (apdu_open_reader): Hacks for PC/SC under Windows.
+
+2003-10-20 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_checkpin): New.
+ (register_commands): Add command CHECKPIN.
+ * app.c (app_check_pin): New.
+ * app-openpgp.c (check_against_given_fingerprint): New. Factored
+ out that code elsewhere.
+ (do_check_pin): New.
+
+2003-10-10 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (ccid_close_reader): New.
+
+ * apdu.c (close_ccid_reader, close_ct_reader, close_csc_reader)
+ (close_osc_reader, apdu_close_reader): New. Not all are properly
+ implemented yet.
+
+2003-10-09 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (ccid_transceive): Add T=1 chaining for sending.
+
+2003-10-08 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (do_getattr): Support SERIALNO and AID.
+
+2003-10-01 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c: Detect GnuPG 1.3 and include appropriate files.
+ * apdu.c: Ditto.
+ * app-openpgp.c: Ditto.
+ * iso7816.c: Ditto.
+ (generate_keypair): Renamed to ..
+ (do_generate_keypair): .. this.
+ * app-common.h [GNUPG_MAJOR_VERSION]: New.
+ * iso7816.h [GNUPG_MAJOR_VERSION]: Include cardglue.h
+
+2003-09-30 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_getattr): New command GETATTR.
+ * app.c (app_setattr): New.
+ (do_getattr): New.
+ (do_learn_status): Reimplemented in terms of do_getattr.
+
+ * app-openpgp.c (do_change_pin): Make sure CVH1 and CHV2 are
+ always synced.
+ (verify_chv2, verify_chv3): New. Factored out common code.
+ (do_setattr, do_sign, do_auth, do_decipher): Change the names of
+ the prompts to match that we have only 2 different PINs.
+ (app_select_openpgp): Check whether the card enforced CHV1.
+ (convert_sig_counter_value): New. Factor out code from
+ get_sig_counter.
+
+2003-09-28 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (dump_all_do): Use gpg_err_code and not gpg_error.
+
+2003-09-19 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (parse_ccid_descriptor): New.
+ (read_device_info): New.
+ (ccid_open_reader): Check that the device has all required features.
+
+2003-09-06 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c (main): --pcsc-driver again defaults to pcsclite.
+ David Corcoran was so kind to remove the GPL incompatible
+ advertisng clause from pcsclite.
+ * apdu.c (apdu_open_reader): Actually make pcsc-driver option work.
+
+2003-09-05 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c: More work, data can now actually be retrieved.
+ * ccid-driver.c, ccid-driver.h: Alternativley allow use under BSD
+ conditions.
+
+2003-09-02 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c, scdaemon.h: New option --pcsc-ccid.
+ * ccid-driver.c, ccid-driver.h: New but far from being useful.
+ * Makefile.am: Add above.
+ * apdu.c: Add support for that ccid driver.
+
+2003-08-26 Timo Schulz <twoaday@freakmail.de>
+
+ * apdu.c (new_reader_slot): Only set 'is_osc' when OpenSC
+ is used.
+
+2003-08-25 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_setattr): Use a copy of LINE.
+ (cmd_genkey): Use a copy of KEYNO.
+ (cmd_passwd): Use a copy of CHVNOSTR.
+ (cmd_pksign, cmd_pkauth, cmd_pkdecrypt): s/strdup/xtrystrdup/.
+
+2003-08-19 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c, scdaemon.h: New option --pcsc-driver.
+ * apdu.c (apdu_open_reader): Use that option here instead of a
+ hardcoded one.
+
+2003-08-18 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Add OPENSC_LIBS to all programs.
+
+ * scdaemon.c, scdaemon.h: New option --disable-opensc.
+ * card.c (card_open): Implement it.
+ * apdu.c (open_osc_reader, osc_send_apdu): New.
+ (apdu_open_reader) [HAVE_OPENSC]: Use the opensc driver if not
+ disabled.
+ (error_string) [HAVE_OPENSC]: Use sc_strerror.
+ (send_apdu) [HAVE_OPENSC]: Call osc_apdu_send.
+
+ * card-p15.c (p15_enum_keypairs, p15_prepare_key): Adjusted for
+ libgpg-error.
+
+2003-08-14 Timo Schulz <twoaday@freakmail.de>
+
+ * apdu.c (ct_activate_card): Change the code a little to avoid
+ problems with other readers.
+ * Always use 'dynload.h' instead of 'dlfcn.h'.
+
+2003-08-05 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (dump_all_do): Don't analyze constructed DOs after
+ an error.
+
+2003-08-04 Werner Koch <wk@gnupg.org>
+
+ * app.c (app_set_default_reader_port): New.
+ (select_application): Use it here.
+ * scdaemon.c (main): and here.
+ * sc-copykeys.c: --reader-port does now take a string.
+ * sc-investigate.c, scdaemon.c: Ditto.
+ * apdu.c (apdu_open_reader): Ditto. Load pcsclite if no ctapi
+ driver is configured. Always include code for ctapi.
+ (new_reader_slot): Don't test for already used ports and remove
+ port arg.
+ (open_pcsc_reader, pcsc_send_apdu, pcsc_error_string): New.
+ (apdu_send_le): Changed RC to long to cope with PC/SC.
+
+ * scdaemon.c, scdaemon.h: New option --ctapi-driver.
+ * sc-investigate.c, sc-copykeys.c: Ditto.
+
+2003-07-31 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (scdaemon_LDADD): Added INTLLIBS.
+
+2003-07-28 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (do_setattr): Change implementation. Allow all
+ useful DOs.
+
+2003-07-27 Werner Koch <wk@gnupg.org>
+
+ Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
+
+2003-07-24 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (do_learn_status): Print more status information.
+ (app_select_openpgp): Store the card version.
+ (store_fpr): Add argument card_version and fix DOs for old cards.
+ (app_openpgp_storekey): Likewise.
+
+2003-07-23 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_pkauth): New.
+ (cmd_setdata): Check whether data was given at all to avoid
+ passing 0 to malloc.
+
+ * app.c (app_auth): New.
+ * app-openpgp.c (do_auth): New.
+
+2003-07-22 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_passwd): New.
+ * app.c (app_change_pin): New.
+ * app-openpgp.c (do_change_pin): New.
+ * iso7816.c (iso7816_reset_retry_counter): Implemented.
+
+ * sc-investigate.c (main): New option --gen-random.
+ * iso7816.c (iso7816_get_challenge): Don't create APDUs with a
+ length larger than 255.
+
+2003-07-17 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_random): New command RANDOM.
+
+ * iso7816.c (map_sw): New. Use it in this file to return
+ meaningful error messages. Changed all public fucntions to return
+ a gpg_error_t.
+ (iso7816_change_reference_data): New.
+ * apdu.c (apdu_open_reader): Use faked status words for soem
+ system errors.
+
+2003-07-16 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (apdu_send_simple): Use apdu_send_le so that we can
+ specify not to send Le as it should be.
+
+2003-07-15 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Add sc-copykeys program.
+ * sc-copykeys.c: New.
+ * app-openpgp.c (app_openpgp_storekey): New.
+ (app_openpgp_cardinfo): New.
+ (count_bits): New.
+ (store_fpr): And use it here to get the actual length in bit.
+
+2003-07-03 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (do_setattr): Add setting of the URL.
+ (app_select_openpgp): Dump card data only in very verbose mode.
+ (do_decipher): New.
+
+2003-07-02 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (get_sig_counter): New.
+ (do_sign): Print the signature counter and enable the PIN callback.
+ (do_genkey): Implement the PIN callback.
+
+2003-07-01 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (store_fpr): Fixed fingerprint calculation.
+
+2003-06-26 Werner Koch <wk@gnupg.org>
+
+ * app-openpgp.c (find_tlv): Fixed length header parsing.
+
+ * app.c (app_genkey): New.
+ * command.c (cmd_genkey): New.
+
+2003-06-25 Werner Koch <wk@gnupg.org>
+
+ * command.c (percent_plus_unescape): New.
+ (cmd_setattr): New.
+
+2003-06-24 Werner Koch <wk@gnupg.org>
+
+ * command.c (send_status_info): New.
+
+ * app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
+ and setup the function pointers in APP on success. Changed callers.
+ * app.c: New.
+ * app-common.h: New.
+ * scdaemon.h (APP): New type to handle applications.
+ (server_control_s): Add an APP context field.
+
+ * command.c (cmd_serialno): Handle applications.
+ (cmd_pksign): Ditto.
+ (cmd_pkdecrypt): Ditto.
+ (reset_notify): Ditto.
+ (cmd_learn): For now return error for application contexts.
+ (cmd_readcert): Ditto.
+ (cmd_readkey): Ditto.
+
+2003-06-04 Werner Koch <wk@gnupg.org>
+
+ * card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
+
+ Renamed error codes from INVALID to INV and removed _ERROR suffixes.
+
+2003-06-03 Werner Koch <wk@gnupg.org>
+
+ Changed all error codes in all files to the new libgpg-error scheme.
+
+ * scdaemon.h: Include gpg-error.h and errno.h
+ * card.c (map_sc_err): Use unknown for the error source.
+ * Makefile.am: Link with libgpg-error
+
+2003-05-14 Werner Koch <wk@gnupg.org>
+
+ * atr.c, atr.h: New.
+ * sc-investigate.c: Dump the ATR in a human readable format.
+
+2003-05-08 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.h (DBG_CARD_IO_VALUE): New.
+
+ * sc-investigate.c: New.
+ * scdaemon.c (main): Removed --print-atr option.
+
+ * iso7816.c, iso7816.h, app-openpgp.c: New.
+
+2003-04-29 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c: New options --print-atr and --reader-port
+ * apdu.c, apdu.h: New
+
+ * card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC.
+
+ * Makefile.am (LDFLAGS): Removed.
+
+ * command.c (register_commands): Adjusted for new Assuan semantics.
+
+2002-08-21 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c (main): New option --daemon so that the program is
+ not accidently started in the background.
+
+2002-08-16 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c: Include i18n.h.
+
+ * card-common.h (struct p15_private_s): Forward declaration. Add
+ it to card_ctx_s.
+ * card.c (card_close): Make sure private data is released.
+ (card_enum_certs): New.
+ * card-p15.c (p15_release_private_data): New.
+ (init_private_data): New to work around an OpenSC weirdness.
+ (p15_enum_keypairs): Do an OpenSC get_objects only once.
+ (p15_enum_certs): New.
+ (card_p15_bind): Bind new function.
+ * command.c (cmd_learn): Return information about the certificates.
+
+2002-08-09 Werner Koch <wk@gnupg.org>
+
+ * card.c (card_get_serial_and_stamp): Use the tokeinfo serial
+ number as a fallback. Add a special prefix for serial numbers.
+
+2002-07-30 Werner Koch <wk@gnupg.org>
+
+ Changes to cope with OpenSC 0.7.0:
+
+ * card.c: Removed the check for the packed opensc version.
+ Changed include file names of opensc.
+ (map_sc_err): Adjusted error codes for new opensc version.
+ * card-p15.c: Changed include filename of opensc.
+ * card-dinsig.c: Ditto.
+
+ * card-p15.c (p15_decipher): Add flags argument to OpenSC call.
+
+2002-07-24 Werner Koch <wk@gnupg.org>
+
+ * card.c (find_simple_tlv, find_iccsn): New.
+ (card_get_serial_and_stamp): Improved serial number parser.
+
+2002-06-27 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
+
+2002-06-15 Werner Koch <wk@gnupg.org>
+
+ * card-dinsig.c: Documented some stuff from the DIN norm.
+
+2002-04-15 Werner Koch <wk@gnupg.org>
+
+ * command.c (cmd_pksign, cmd_pkdecrypt): Use a copy of the key ID.
+
+2002-04-12 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c: New option --debug-sc N.
+ * card.c (card_open): set it here.
+
+ * card-p15.c (p15_prepare_key): Factored out common code from ...
+ (p15_sign, p15_decipher): here and made the decryption work the
+ regular way.
+
+2002-04-10 Werner Koch <wk@gnupg.org>
+
+ * card.c (card_open): Return immediately when no reader is available.
+
+2002-03-27 Werner Koch <wk@gnupg.org>
+
+ * card.c (card_open, card_close): Adjusted for changes in OpenSC.
+
+2002-03-10 Werner Koch <wk@gnupg.org>
+
+ * card-p15.c, card-dinsig.c, card-common.h: New.
+ * card.c: Factored most code out to the new modules, so that we
+ can better support different types of card applications.
+
+2002-01-26 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.c scdaemon.h, command.c: New. Based on the code from
+ the gpg-agent.
+
+
+ Copyright 2002, 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+Local Variables:
+buffer-read-only: t
+End:
diff --git a/scd/Makefile.am b/scd/Makefile.am
new file mode 100644
index 0000000..32be6ab
--- /dev/null
+++ b/scd/Makefile.am
@@ -0,0 +1,51 @@
+# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = ChangeLog-2011 scdaemon-w32info.rc scdaemon.w32-manifest.in
+
+libexec_PROGRAMS = scdaemon
+
+AM_CPPFLAGS = $(LIBUSB_CPPFLAGS)
+
+include $(top_srcdir)/am/cmacros.am
+
+if HAVE_W32_SYSTEM
+scdaemon_robjs = $(resource_objs) scdaemon-w32info.o
+endif
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+
+
+card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c app-sc-hsm.c
+
+scdaemon_SOURCES = \
+ scdaemon.c scdaemon.h \
+ command.c \
+ atr.c atr.h \
+ apdu.c apdu.h \
+ ccid-driver.c ccid-driver.h \
+ iso7816.c iso7816.h \
+ app.c app-common.h app-help.c $(card_apps)
+
+
+scdaemon_LDADD = $(libcommonpth) \
+ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
+ $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_robjs)
diff --git a/scd/Makefile.in b/scd/Makefile.in
new file mode 100644
index 0000000..d278ee5
--- /dev/null
+++ b/scd/Makefile.in
@@ -0,0 +1,846 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+# cmacros.am - C macro definitions
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# GnuPG is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = scdaemon$(EXEEXT)
+@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \
+@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\""
+
+
+# If a specific protect tool program has been defined, pass its name
+# to cc. Note that these macros should not be used directly but via
+# the gnupg_module_name function.
+@GNUPG_AGENT_PGM_TRUE@am__append_2 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\""
+@GNUPG_PINENTRY_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\""
+@GNUPG_SCDAEMON_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
+@GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
+@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\""
+@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\""
+subdir = scd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \
+ $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \
+ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \
+ $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \
+ $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = scdaemon.w32-manifest
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am__objects_1 = app-openpgp.$(OBJEXT) app-nks.$(OBJEXT) \
+ app-dinsig.$(OBJEXT) app-p15.$(OBJEXT) app-geldkarte.$(OBJEXT) \
+ app-sc-hsm.$(OBJEXT)
+am_scdaemon_OBJECTS = scdaemon.$(OBJEXT) command.$(OBJEXT) \
+ atr.$(OBJEXT) apdu.$(OBJEXT) ccid-driver.$(OBJEXT) \
+ iso7816.$(OBJEXT) app.$(OBJEXT) app-help.$(OBJEXT) \
+ $(am__objects_1)
+scdaemon_OBJECTS = $(am_scdaemon_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \
+@HAVE_W32_SYSTEM_TRUE@ scdaemon-w32info.o
+scdaemon_DEPENDENCIES = $(libcommonpth) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/apdu.Po ./$(DEPDIR)/app-dinsig.Po \
+ ./$(DEPDIR)/app-geldkarte.Po ./$(DEPDIR)/app-help.Po \
+ ./$(DEPDIR)/app-nks.Po ./$(DEPDIR)/app-openpgp.Po \
+ ./$(DEPDIR)/app-p15.Po ./$(DEPDIR)/app-sc-hsm.Po \
+ ./$(DEPDIR)/app.Po ./$(DEPDIR)/atr.Po \
+ ./$(DEPDIR)/ccid-driver.Po ./$(DEPDIR)/command.Po \
+ ./$(DEPDIR)/iso7816.Po ./$(DEPDIR)/scdaemon.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(scdaemon_SOURCES)
+DIST_SOURCES = $(scdaemon_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/scdaemon.w32-manifest.in $(top_srcdir)/am/cmacros.am \
+ $(top_srcdir)/build-aux/depcomp \
+ $(top_srcdir)/build-aux/mkinstalldirs
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+AWK_HEX_NUMBER_OPTION = @AWK_HEX_NUMBER_OPTION@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_HOSTNAME = @BUILD_HOSTNAME@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+BUILD_VERSION = @BUILD_VERSION@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DL_LIBS = @DL_LIBS@
+DNSLIBS = @DNSLIBS@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENCFS = @ENCFS@
+EXEEXT = @EXEEXT@
+FUSERMOUNT = @FUSERMOUNT@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@
+GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@
+GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@
+GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@
+GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@
+GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@
+GPGKEYS_LDAP = @GPGKEYS_LDAP@
+GPGRT_CONFIG = @GPGRT_CONFIG@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+KSBA_CFLAGS = @KSBA_CFLAGS@
+KSBA_CONFIG = @KSBA_CONFIG@
+KSBA_LIBS = @KSBA_LIBS@
+LBER_LIBS = @LBER_LIBS@
+LDAPLIBS = @LDAPLIBS@
+LDAP_CPPFLAGS = @LDAP_CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
+LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBREADLINE = @LIBREADLINE@
+LIBS = @LIBS@
+LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@
+LIBUSB_LIBS = @LIBUSB_LIBS@
+LIBUTIL_LIBS = @LIBUTIL_LIBS@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NETLIBS = @NETLIBS@
+NPTH_CFLAGS = @NPTH_CFLAGS@
+NPTH_CONFIG = @NPTH_CONFIG@
+NPTH_LIBS = @NPTH_LIBS@
+NTBTLS_CFLAGS = @NTBTLS_CFLAGS@
+NTBTLS_CONFIG = @NTBTLS_CONFIG@
+NTBTLS_LIBS = @NTBTLS_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_GT = @PACKAGE_GT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SHRED = @SHRED@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
+SQLITE3_LIBS = @SQLITE3_LIBS@
+STRIP = @STRIP@
+SYSROOT = @SYSROOT@
+SYS_SOCKET_H = @SYS_SOCKET_H@
+TAR = @TAR@
+USE_C99_CFLAGS = @USE_C99_CFLAGS@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+W32SOCKLIBS = @W32SOCKLIBS@
+WINDRES = @WINDRES@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+YAT2M = @YAT2M@
+ZLIBS = @ZLIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = $(datadir)/locale
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = ChangeLog-2011 scdaemon-w32info.rc scdaemon.w32-manifest.in
+
+# NB: AM_CFLAGS may also be used by tools running on the build
+# platform to create source files.
+AM_CPPFLAGS = $(LIBUSB_CPPFLAGS) -DLOCALEDIR=\"$(localedir)\" \
+ $(am__append_1) $(am__append_2) $(am__append_3) \
+ $(am__append_4) $(am__append_5) $(am__append_6) \
+ $(am__append_7)
+@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs =
+
+# Under Windows we use LockFileEx. WindowsCE provides this only on
+# the WindowsMobile 6 platform and thus we need to use the coredll6
+# import library. We also want to use a stacksize of 256k instead of
+# the 2MB which is the default with cegcc. 256k is the largest stack
+# we use with pth.
+@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6
+@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags =
+@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000
+resource_objs =
+
+# Convenience macros
+libcommon = ../common/libcommon.a
+libcommonpth = ../common/libcommonpth.a
+libcommontls = ../common/libcommontls.a
+libcommontlsnpth = ../common/libcommontlsnpth.a
+@HAVE_W32_SYSTEM_TRUE@scdaemon_robjs = $(resource_objs) scdaemon-w32info.o
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+
+card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c app-sc-hsm.c
+scdaemon_SOURCES = \
+ scdaemon.c scdaemon.h \
+ command.c \
+ atr.c atr.h \
+ apdu.c apdu.h \
+ ccid-driver.c ccid-driver.h \
+ iso7816.c iso7816.h \
+ app.c app-common.h app-help.c $(card_apps)
+
+scdaemon_LDADD = $(libcommonpth) \
+ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+ $(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
+ $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(scdaemon_robjs)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj .rc
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu scd/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/am/cmacros.am $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+scdaemon.w32-manifest: $(top_builddir)/config.status $(srcdir)/scdaemon.w32-manifest.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ -test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS)
+
+scdaemon$(EXEEXT): $(scdaemon_OBJECTS) $(scdaemon_DEPENDENCIES) $(EXTRA_scdaemon_DEPENDENCIES)
+ @rm -f scdaemon$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(scdaemon_OBJECTS) $(scdaemon_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apdu.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-dinsig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-geldkarte.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-help.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-nks.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-openpgp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-p15.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-sc-hsm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ccid-driver.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iso7816.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scdaemon.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/apdu.Po
+ -rm -f ./$(DEPDIR)/app-dinsig.Po
+ -rm -f ./$(DEPDIR)/app-geldkarte.Po
+ -rm -f ./$(DEPDIR)/app-help.Po
+ -rm -f ./$(DEPDIR)/app-nks.Po
+ -rm -f ./$(DEPDIR)/app-openpgp.Po
+ -rm -f ./$(DEPDIR)/app-p15.Po
+ -rm -f ./$(DEPDIR)/app-sc-hsm.Po
+ -rm -f ./$(DEPDIR)/app.Po
+ -rm -f ./$(DEPDIR)/atr.Po
+ -rm -f ./$(DEPDIR)/ccid-driver.Po
+ -rm -f ./$(DEPDIR)/command.Po
+ -rm -f ./$(DEPDIR)/iso7816.Po
+ -rm -f ./$(DEPDIR)/scdaemon.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/apdu.Po
+ -rm -f ./$(DEPDIR)/app-dinsig.Po
+ -rm -f ./$(DEPDIR)/app-geldkarte.Po
+ -rm -f ./$(DEPDIR)/app-help.Po
+ -rm -f ./$(DEPDIR)/app-nks.Po
+ -rm -f ./$(DEPDIR)/app-openpgp.Po
+ -rm -f ./$(DEPDIR)/app-p15.Po
+ -rm -f ./$(DEPDIR)/app-sc-hsm.Po
+ -rm -f ./$(DEPDIR)/app.Po
+ -rm -f ./$(DEPDIR)/atr.Po
+ -rm -f ./$(DEPDIR)/ccid-driver.Po
+ -rm -f ./$(DEPDIR)/command.Po
+ -rm -f ./$(DEPDIR)/iso7816.Po
+ -rm -f ./$(DEPDIR)/scdaemon.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libexecPROGRAMS install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+@HAVE_W32_SYSTEM_TRUE@.rc.o:
+@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@"
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/scd/apdu.c b/scd/apdu.c
new file mode 100644
index 0000000..9568d25
--- /dev/null
+++ b/scd/apdu.c
@@ -0,0 +1,3523 @@
+/* apdu.c - ISO 7816 APDU functions and low level I/O
+ * Copyright (C) 2003, 2004, 2008, 2009, 2010,
+ * 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* NOTE: This module is also used by other software, thus the use of
+ the macro USE_NPTH is mandatory. For GnuPG this macro is
+ guaranteed to be defined true. */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifdef USE_NPTH
+# include <unistd.h>
+# include <fcntl.h>
+# include <npth.h>
+#endif
+
+
+/* If requested include the definitions for the remote APDU protocol
+ code. */
+#ifdef USE_G10CODE_RAPDU
+#include "rapdu.h"
+#endif /*USE_G10CODE_RAPDU*/
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
+/* This is used with GnuPG version < 1.9. The code has been source
+ copied from the current GnuPG >= 1.9 and is maintained over
+ there. */
+#include "../common/options.h"
+#include "errors.h"
+#include "memory.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "dynload.h"
+#include "cardglue.h"
+#else /* GNUPG_MAJOR_VERSION != 1 */
+#include "scdaemon.h"
+#include "../common/exechelp.h"
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+#include "../common/host2net.h"
+#include "../common/membuf.h"
+
+#include "iso7816.h"
+#include "apdu.h"
+#define CCID_DRIVER_INCLUDE_USB_IDS 1
+#include "ccid-driver.h"
+
+struct dev_list {
+ void *table;
+ const char *portstr;
+ int idx;
+ int idx_max;
+};
+
+#define MAX_READER 4 /* Number of readers we support concurrently. */
+
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define DLSTDCALL __stdcall
+#else
+#define DLSTDCALL
+#endif
+
+#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
+typedef unsigned int pcsc_dword_t;
+#else
+typedef unsigned long pcsc_dword_t;
+#endif
+
+/* PC/SC context to access readers. Shared among all readers. */
+static struct pcsc {
+ unsigned int context_valid:1;
+ long context;
+ char *reader_list; /* List of detected readers. */
+} pcsc;
+
+/* A structure to collect information pertaining to one reader
+ slot. */
+struct reader_table_s {
+ int used; /* True if slot is used. */
+ unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */
+
+ /* Function pointers initialized to the various backends. */
+ int (*connect_card)(int);
+ int (*disconnect_card)(int);
+ int (*close_reader)(int);
+ int (*reset_reader)(int);
+ int (*get_status_reader)(int, unsigned int *, int);
+ int (*send_apdu_reader)(int,unsigned char *,size_t,
+ unsigned char *, size_t *, pininfo_t *);
+ int (*check_pinpad)(int, int, pininfo_t *);
+ void (*dump_status_reader)(int);
+ int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
+ int (*set_prompt_cb)(int, void (*) (void *, int), void*);
+ int (*pinpad_verify)(int, int, int, int, int, pininfo_t *);
+ int (*pinpad_modify)(int, int, int, int, int, pininfo_t *);
+
+ struct {
+ ccid_driver_t handle;
+ } ccid;
+ struct {
+ long card;
+ pcsc_dword_t protocol;
+ pcsc_dword_t verify_ioctl;
+ pcsc_dword_t modify_ioctl;
+ int pinmin;
+ int pinmax;
+ pcsc_dword_t current_state;
+ } pcsc;
+#ifdef USE_G10CODE_RAPDU
+ struct {
+ rapdu_t handle;
+ } rapdu;
+#endif /*USE_G10CODE_RAPDU*/
+ char *rdrname; /* Name of the connected reader or NULL if unknown. */
+ unsigned int is_t0:1; /* True if we know that we are running T=0. */
+ unsigned int is_spr532:1; /* True if we know that the reader is a SPR532. */
+ unsigned int pinpad_varlen_supported:1; /* True if we know that the reader
+ supports variable length pinpad
+ input. */
+ unsigned int require_get_status:1;
+ unsigned char atr[33];
+ size_t atrlen; /* A zero length indicates that the ATR has
+ not yet been read; i.e. the card is not
+ ready for use. */
+#ifdef USE_NPTH
+ npth_mutex_t lock;
+#endif
+};
+typedef struct reader_table_s *reader_table_t;
+
+/* A global table to keep track of active readers. */
+static struct reader_table_s reader_table[MAX_READER];
+
+#ifdef USE_NPTH
+static npth_mutex_t reader_table_lock;
+#endif
+
+
+/* PC/SC constants and function pointer. */
+#define PCSC_SCOPE_USER 0
+#define PCSC_SCOPE_TERMINAL 1
+#define PCSC_SCOPE_SYSTEM 2
+#define PCSC_SCOPE_GLOBAL 3
+
+#define PCSC_PROTOCOL_T0 1
+#define PCSC_PROTOCOL_T1 2
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_PROTOCOL_RAW 0x00010000 /* The active protocol. */
+#else
+# define PCSC_PROTOCOL_RAW 4
+#endif
+
+#define PCSC_SHARE_EXCLUSIVE 1
+#define PCSC_SHARE_SHARED 2
+#define PCSC_SHARE_DIRECT 3
+
+#define PCSC_LEAVE_CARD 0
+#define PCSC_RESET_CARD 1
+#define PCSC_UNPOWER_CARD 2
+#define PCSC_EJECT_CARD 3
+
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_UNKNOWN 0x0000 /* The driver is not aware of the status. */
+# define PCSC_ABSENT 0x0001 /* Card is absent. */
+# define PCSC_PRESENT 0x0002 /* Card is present. */
+# define PCSC_SWALLOWED 0x0003 /* Card is present and electrical connected. */
+# define PCSC_POWERED 0x0004 /* Card is powered. */
+# define PCSC_NEGOTIABLE 0x0005 /* Card is awaiting PTS. */
+# define PCSC_SPECIFIC 0x0006 /* Card is ready for use. */
+#else
+# define PCSC_UNKNOWN 0x0001
+# define PCSC_ABSENT 0x0002 /* Card is absent. */
+# define PCSC_PRESENT 0x0004 /* Card is present. */
+# define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
+# define PCSC_POWERED 0x0010 /* Card is powered. */
+# define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
+# define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
+#endif
+
+#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
+#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
+#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
+#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
+#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
+#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
+#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
+#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
+#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
+#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
+#ifdef HAVE_W32_SYSTEM
+# define PCSC_STATE_UNPOWERED 0x0400 /* Card not powerred up. */
+#endif
+
+/* Some PC/SC error codes. */
+#define PCSC_E_CANCELLED 0x80100002
+#define PCSC_E_CANT_DISPOSE 0x8010000E
+#define PCSC_E_INSUFFICIENT_BUFFER 0x80100008
+#define PCSC_E_INVALID_ATR 0x80100015
+#define PCSC_E_INVALID_HANDLE 0x80100003
+#define PCSC_E_INVALID_PARAMETER 0x80100004
+#define PCSC_E_INVALID_TARGET 0x80100005
+#define PCSC_E_INVALID_VALUE 0x80100011
+#define PCSC_E_NO_MEMORY 0x80100006
+#define PCSC_E_UNKNOWN_READER 0x80100009
+#define PCSC_E_TIMEOUT 0x8010000A
+#define PCSC_E_SHARING_VIOLATION 0x8010000B
+#define PCSC_E_NO_SMARTCARD 0x8010000C
+#define PCSC_E_UNKNOWN_CARD 0x8010000D
+#define PCSC_E_PROTO_MISMATCH 0x8010000F
+#define PCSC_E_NOT_READY 0x80100010
+#define PCSC_E_SYSTEM_CANCELLED 0x80100012
+#define PCSC_E_NOT_TRANSACTED 0x80100016
+#define PCSC_E_READER_UNAVAILABLE 0x80100017
+#define PCSC_E_NO_SERVICE 0x8010001D
+#define PCSC_E_NO_READERS_AVAILABLE 0x8010002E
+#define PCSC_E_SERVICE_STOPPED 0x8010001E
+#define PCSC_W_RESET_CARD 0x80100068
+#define PCSC_W_REMOVED_CARD 0x80100069
+
+/* Fix pcsc-lite ABI incompatibility. */
+#ifndef SCARD_CTL_CODE
+#ifdef _WIN32
+#include <winioctl.h>
+#define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD, (code), \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#else
+#define SCARD_CTL_CODE(code) (0x42000000 + (code))
+#endif
+#endif
+
+#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400)
+#define CM_IOCTL_VENDOR_IFD_EXCHANGE SCARD_CTL_CODE(1)
+#define FEATURE_VERIFY_PIN_DIRECT 0x06
+#define FEATURE_MODIFY_PIN_DIRECT 0x07
+#define FEATURE_GET_TLV_PROPERTIES 0x12
+
+#define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2
+#define PCSCv2_PART10_PROPERTY_bTimeOut2 3
+#define PCSCv2_PART10_PROPERTY_bMinPINSize 6
+#define PCSCv2_PART10_PROPERTY_bMaxPINSize 7
+#define PCSCv2_PART10_PROPERTY_wIdVendor 11
+#define PCSCv2_PART10_PROPERTY_wIdProduct 12
+
+
+/* The PC/SC error is defined as a long as per specs. Due to left
+ shifts bit 31 will get sign extended. We use this mask to fix
+ it. */
+#define PCSC_ERR_MASK(a) ((a) & 0xffffffff)
+
+
+struct pcsc_io_request_s
+{
+ unsigned long protocol;
+ unsigned long pci_len;
+};
+
+typedef struct pcsc_io_request_s *pcsc_io_request_t;
+
+#ifdef __APPLE__
+#pragma pack(1)
+#endif
+
+struct pcsc_readerstate_s
+{
+ const char *reader;
+ void *user_data;
+ pcsc_dword_t current_state;
+ pcsc_dword_t event_state;
+ pcsc_dword_t atrlen;
+ unsigned char atr[33];
+};
+
+#ifdef __APPLE__
+#pragma pack()
+#endif
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
+long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope,
+ const void *reserved1,
+ const void *reserved2,
+ long *r_context);
+long (* DLSTDCALL pcsc_release_context) (long context);
+long (* DLSTDCALL pcsc_cancel) (long context);
+long (* DLSTDCALL pcsc_list_readers) (long context,
+ const char *groups,
+ char *readers, pcsc_dword_t*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (long context,
+ pcsc_dword_t timeout,
+ pcsc_readerstate_t readerstates,
+ pcsc_dword_t nreaderstates);
+long (* DLSTDCALL pcsc_connect) (long context,
+ const char *reader,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ long *r_card,
+ pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (long card,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ pcsc_dword_t initialization,
+ pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_disconnect) (long card,
+ pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_status) (long card,
+ char *reader, pcsc_dword_t *readerlen,
+ pcsc_dword_t *r_state,
+ pcsc_dword_t *r_protocol,
+ unsigned char *atr, pcsc_dword_t *atrlen);
+long (* DLSTDCALL pcsc_begin_transaction) (long card);
+long (* DLSTDCALL pcsc_end_transaction) (long card,
+ pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_transmit) (long card,
+ const pcsc_io_request_t send_pci,
+ const unsigned char *send_buffer,
+ pcsc_dword_t send_len,
+ pcsc_io_request_t recv_pci,
+ unsigned char *recv_buffer,
+ pcsc_dword_t *recv_len);
+long (* DLSTDCALL pcsc_set_timeout) (long context,
+ pcsc_dword_t timeout);
+long (* DLSTDCALL pcsc_control) (long card,
+ pcsc_dword_t control_code,
+ const void *send_buffer,
+ pcsc_dword_t send_len,
+ void *recv_buffer,
+ pcsc_dword_t recv_len,
+ pcsc_dword_t *bytes_returned);
+
+
+/* Prototypes. */
+static int pcsc_vendor_specific_init (int slot);
+static int pcsc_get_status (int slot, unsigned int *status, int on_wire);
+static int reset_pcsc_reader (int slot);
+static int apdu_get_status_internal (int slot, int hang, unsigned int *status,
+ int on_wire);
+static int check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo);
+static int pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+static int pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+
+
+
+/*
+ * Helper
+ */
+
+/* Return true if (BUFFER,LENGTH) consists of only binary zeroes. */
+static int
+all_zero_p (const void *buffer, size_t length)
+{
+ const unsigned char *p;
+
+ for (p=buffer; length; p++, length--)
+ if (*p)
+ return 0;
+ return 1;
+}
+
+
+
+static int
+lock_slot (int slot)
+{
+#ifdef USE_NPTH
+ int err;
+
+ err = npth_mutex_lock (&reader_table[slot].lock);
+ if (err)
+ {
+ log_error ("failed to acquire apdu lock: %s\n", strerror (err));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_NPTH*/
+ return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_NPTH
+ int err;
+
+ err = npth_mutex_trylock (&reader_table[slot].lock);
+ if (err == EBUSY)
+ return SW_HOST_BUSY;
+ else if (err)
+ {
+ log_error ("failed to acquire apdu lock: %s\n", strerror (err));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_NPTH*/
+ return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_NPTH
+ int err;
+
+ err = npth_mutex_unlock (&reader_table[slot].lock);
+ if (err)
+ log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_NPTH*/
+}
+
+
+/* Find an unused reader slot for PORTSTR and put it into the reader
+ table. Return -1 on error or the index into the reader table.
+ Acquire slot's lock on successful return. Caller needs to unlock it. */
+static int
+new_reader_slot (void)
+{
+ int i, reader = -1;
+
+ for (i=0; i < MAX_READER; i++)
+ if (!reader_table[i].used)
+ {
+ reader = i;
+ reader_table[reader].used = 1;
+ break;
+ }
+
+ if (reader == -1)
+ {
+ log_error ("new_reader_slot: out of slots\n");
+ return -1;
+ }
+
+ if (lock_slot (reader))
+ {
+ reader_table[reader].used = 0;
+ return -1;
+ }
+
+ reader_table[reader].connect_card = NULL;
+ reader_table[reader].disconnect_card = NULL;
+ reader_table[reader].close_reader = NULL;
+ reader_table[reader].reset_reader = NULL;
+ reader_table[reader].get_status_reader = NULL;
+ reader_table[reader].send_apdu_reader = NULL;
+ reader_table[reader].check_pinpad = check_pcsc_pinpad;
+ reader_table[reader].dump_status_reader = NULL;
+ reader_table[reader].set_progress_cb = NULL;
+ reader_table[reader].set_prompt_cb = NULL;
+ reader_table[reader].pinpad_verify = pcsc_pinpad_verify;
+ reader_table[reader].pinpad_modify = pcsc_pinpad_modify;
+
+ reader_table[reader].is_t0 = 1;
+ reader_table[reader].is_spr532 = 0;
+ reader_table[reader].pinpad_varlen_supported = 0;
+ reader_table[reader].require_get_status = 1;
+ reader_table[reader].pcsc.verify_ioctl = 0;
+ reader_table[reader].pcsc.modify_ioctl = 0;
+ reader_table[reader].pcsc.pinmin = -1;
+ reader_table[reader].pcsc.pinmax = -1;
+ reader_table[reader].pcsc.current_state = PCSC_STATE_UNAWARE;
+
+ return reader;
+}
+
+
+static void
+dump_reader_status (int slot)
+{
+ if (!opt.verbose)
+ return;
+
+ if (reader_table[slot].dump_status_reader)
+ reader_table[slot].dump_status_reader (slot);
+
+ if (reader_table[slot].atrlen)
+ {
+ log_info ("slot %d: ATR=", slot);
+ log_printhex (reader_table[slot].atr, reader_table[slot].atrlen, "");
+ }
+}
+
+
+
+static const char *
+host_sw_string (long err)
+{
+ switch (err)
+ {
+ case 0: return "okay";
+ case SW_HOST_OUT_OF_CORE: return "out of core";
+ case SW_HOST_INV_VALUE: return "invalid value";
+ case SW_HOST_NO_DRIVER: return "no driver";
+ case SW_HOST_NOT_SUPPORTED: return "not supported";
+ case SW_HOST_LOCKING_FAILED: return "locking failed";
+ case SW_HOST_BUSY: return "busy";
+ case SW_HOST_NO_CARD: return "no card";
+ case SW_HOST_CARD_INACTIVE: return "card inactive";
+ case SW_HOST_CARD_IO_ERROR: return "card I/O error";
+ case SW_HOST_GENERAL_ERROR: return "general error";
+ case SW_HOST_NO_READER: return "no reader";
+ case SW_HOST_ABORTED: return "aborted";
+ case SW_HOST_NO_PINPAD: return "no pinpad";
+ case SW_HOST_ALREADY_CONNECTED: return "already connected";
+ case SW_HOST_CANCELLED: return "cancelled";
+ case SW_HOST_USB_OTHER: return "USB general error";
+ case SW_HOST_USB_IO: return "USB I/O error";
+ case SW_HOST_USB_ACCESS: return "USB permission denied";
+ case SW_HOST_USB_NO_DEVICE:return "USB no device";
+ case SW_HOST_USB_BUSY: return "USB busy";
+ case SW_HOST_USB_TIMEOUT: return "USB timeout";
+ case SW_HOST_USB_OVERFLOW: return "USB overflow";
+ default: return "unknown host status error";
+ }
+}
+
+
+const char *
+apdu_strerror (int rc)
+{
+ switch (rc)
+ {
+ case SW_EOF_REACHED : return "eof reached";
+ case SW_EEPROM_FAILURE : return "eeprom failure";
+ case SW_WRONG_LENGTH : return "wrong length";
+ case SW_SM_NOT_SUP : return "secure messaging not supported";
+ case SW_CC_NOT_SUP : return "command chaining not supported";
+ case SW_FILE_STRUCT : return "command can't be used for file structure.";
+ case SW_CHV_WRONG : return "CHV wrong";
+ case SW_CHV_BLOCKED : return "CHV blocked";
+ case SW_REF_DATA_INV : return "referenced data invalidated";
+ case SW_USE_CONDITIONS : return "use conditions not satisfied";
+ case SW_NO_CURRENT_EF : return "no current EF selected";
+ case SW_BAD_PARAMETER : return "bad parameter";
+ case SW_NOT_SUPPORTED : return "not supported";
+ case SW_FILE_NOT_FOUND : return "file not found";
+ case SW_RECORD_NOT_FOUND:return "record not found";
+ case SW_REF_NOT_FOUND : return "reference not found";
+ case SW_NOT_ENOUGH_MEMORY: return "not enough memory space in the file";
+ case SW_INCONSISTENT_LC: return "Lc inconsistent with TLV structure.";
+ case SW_INCORRECT_P0_P1: return "incorrect parameters P0,P1";
+ case SW_BAD_LC : return "Lc inconsistent with P0,P1";
+ case SW_BAD_P0_P1 : return "bad P0,P1";
+ case SW_INS_NOT_SUP : return "instruction not supported";
+ case SW_CLA_NOT_SUP : return "class not supported";
+ case SW_SUCCESS : return "success";
+ default:
+ if ((rc & ~0x00ff) == SW_MORE_DATA)
+ return "more data available";
+ if ( (rc & 0x10000) )
+ return host_sw_string (rc);
+ return "unknown status error";
+ }
+}
+
+/*
+ PC/SC Interface
+ */
+
+static const char *
+pcsc_error_string (long err)
+{
+ const char *s;
+
+ if (!err)
+ return "okay";
+ if ((err & 0x80100000) != 0x80100000)
+ return "invalid PC/SC error code";
+ err &= 0xffff;
+ switch (err)
+ {
+ case 0x0002: s = "cancelled"; break;
+ case 0x000e: s = "can't dispose"; break;
+ case 0x0008: s = "insufficient buffer"; break;
+ case 0x0015: s = "invalid ATR"; break;
+ case 0x0003: s = "invalid handle"; break;
+ case 0x0004: s = "invalid parameter"; break;
+ case 0x0005: s = "invalid target"; break;
+ case 0x0011: s = "invalid value"; break;
+ case 0x0006: s = "no memory"; break;
+ case 0x0013: s = "comm error"; break;
+ case 0x0001: s = "internal error"; break;
+ case 0x0014: s = "unknown error"; break;
+ case 0x0007: s = "waited too long"; break;
+ case 0x0009: s = "unknown reader"; break;
+ case 0x000a: s = "timeout"; break;
+ case 0x000b: s = "sharing violation"; break;
+ case 0x000c: s = "no smartcard"; break;
+ case 0x000d: s = "unknown card"; break;
+ case 0x000f: s = "proto mismatch"; break;
+ case 0x0010: s = "not ready"; break;
+ case 0x0012: s = "system cancelled"; break;
+ case 0x0016: s = "not transacted"; break;
+ case 0x0017: s = "reader unavailable"; break;
+ case 0x0065: s = "unsupported card"; break;
+ case 0x0066: s = "unresponsive card"; break;
+ case 0x0067: s = "unpowered card"; break;
+ case 0x0068: s = "reset card"; break;
+ case 0x0069: s = "removed card"; break;
+ case 0x006a: s = "inserted card"; break;
+ case 0x001f: s = "unsupported feature"; break;
+ case 0x0019: s = "PCI too small"; break;
+ case 0x001a: s = "reader unsupported"; break;
+ case 0x001b: s = "duplicate reader"; break;
+ case 0x001c: s = "card unsupported"; break;
+ case 0x001d: s = "no service"; break;
+ case 0x001e: s = "service stopped"; break;
+ case 0x002e: s = "no readers available"; break;
+ default: s = "unknown PC/SC error code"; break;
+ }
+ return s;
+}
+
+/* Map PC/SC error codes to our special host status words. */
+static int
+pcsc_error_to_sw (long ec)
+{
+ int rc;
+
+ switch ( PCSC_ERR_MASK (ec) )
+ {
+ case 0: rc = 0; break;
+
+ case PCSC_E_CANCELLED: rc = SW_HOST_CANCELLED; break;
+ case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break;
+ case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
+ case PCSC_E_NO_SERVICE:
+ case PCSC_E_SERVICE_STOPPED:
+ case PCSC_E_UNKNOWN_READER: rc = SW_HOST_NO_READER; break;
+ case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break;
+ case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break;
+ case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break;
+
+ case PCSC_E_INVALID_TARGET:
+ case PCSC_E_INVALID_VALUE:
+ case PCSC_E_INVALID_HANDLE:
+ case PCSC_E_INVALID_PARAMETER:
+ case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
+
+ default: rc = SW_HOST_GENERAL_ERROR; break;
+ }
+
+ return rc;
+}
+
+static void
+dump_pcsc_reader_status (int slot)
+{
+ if (reader_table[slot].pcsc.card)
+ {
+ log_info ("reader slot %d: active protocol:", slot);
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+ log_printf (" T0");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ log_printf (" T1");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+ log_printf (" raw");
+ log_printf ("\n");
+ }
+ else
+ log_info ("reader slot %d: not connected\n", slot);
+}
+
+
+static int
+pcsc_get_status (int slot, unsigned int *status, int on_wire)
+{
+ long err;
+ struct pcsc_readerstate_s rdrstates[1];
+
+ (void)on_wire;
+ memset (rdrstates, 0, sizeof *rdrstates);
+ rdrstates[0].reader = reader_table[slot].rdrname;
+ rdrstates[0].current_state = reader_table[slot].pcsc.current_state;
+ err = pcsc_get_status_change (pcsc.context, 0, rdrstates, 1);
+ if (err == PCSC_E_TIMEOUT)
+ err = 0; /* Timeout is no error here. */
+ if (err)
+ {
+ log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ if ((rdrstates[0].event_state & PCSC_STATE_CHANGED))
+ reader_table[slot].pcsc.current_state =
+ (rdrstates[0].event_state & ~PCSC_STATE_CHANGED);
+
+ if (DBG_READER)
+ log_debug
+ ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n",
+ (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"",
+ (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"",
+ (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"",
+ (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"",
+ (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"",
+ (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"",
+ (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"",
+ (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"",
+ (rdrstates[0].event_state & PCSC_STATE_INUSE)? " inuse":"",
+ (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" );
+
+ *status = 0;
+ if ( (reader_table[slot].pcsc.current_state & PCSC_STATE_PRESENT) )
+ {
+ *status |= APDU_CARD_PRESENT;
+ if ( !(reader_table[slot].pcsc.current_state & PCSC_STATE_MUTE) )
+ *status |= APDU_CARD_ACTIVE;
+ }
+#ifndef HAVE_W32_SYSTEM
+ /* We indicate a useful card if it is not in use by another
+ application. This is because we only use exclusive access
+ mode. */
+ if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
+ && (opt.pcsc_shared
+ || !(reader_table[slot].pcsc.current_state & PCSC_STATE_INUSE)))
+ *status |= APDU_CARD_USABLE;
+#else
+ /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
+ time when we are the only user (SCM SCR335) under Windows. */
+ if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
+ *status |= APDU_CARD_USABLE;
+#endif
+
+ if (!on_wire && (rdrstates[0].event_state & PCSC_STATE_CHANGED))
+ /* Event like sleep/resume occurs, which requires RESET. */
+ return SW_HOST_NO_READER;
+ else
+ return 0;
+}
+
+
+/* Send the APDU of length APDULEN to SLOT and return a maximum of
+ *BUFLEN data in BUFFER, the actual returned size will be stored at
+ BUFLEN. Returns: A status word. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ pininfo_t *pininfo)
+{
+ long err;
+ struct pcsc_io_request_s send_pci;
+ pcsc_dword_t recv_len;
+
+ (void)pininfo;
+
+ if (!reader_table[slot].atrlen
+ && (err = reset_pcsc_reader (slot)))
+ return err;
+
+ if (DBG_CARD_IO)
+ log_printhex (apdu, apdulen, " PCSC_data:");
+
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ send_pci.protocol = PCSC_PROTOCOL_T1;
+ else
+ send_pci.protocol = PCSC_PROTOCOL_T0;
+ send_pci.pci_len = sizeof send_pci;
+ recv_len = *buflen;
+ err = pcsc_transmit (reader_table[slot].pcsc.card,
+ &send_pci, apdu, apdulen,
+ NULL, buffer, &recv_len);
+ *buflen = recv_len;
+ if (err)
+ log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+
+ /* Handle fatal errors which require shutdown of reader. */
+ if (err == PCSC_E_NOT_TRANSACTED || err == PCSC_W_RESET_CARD
+ || err == PCSC_W_REMOVED_CARD)
+ {
+ reader_table[slot].pcsc.current_state = PCSC_STATE_UNAWARE;
+ scd_kick_the_loop ();
+ }
+
+ return pcsc_error_to_sw (err);
+}
+
+
+/* Do some control with the value of IOCTL_CODE to the card inserted
+ to SLOT. Input buffer is specified by CNTLBUF of length LEN.
+ Output buffer is specified by BUFFER of length *BUFLEN, and the
+ actual output size will be stored at BUFLEN. Returns: A status word.
+ This routine is used for PIN pad input support. */
+static int
+control_pcsc (int slot, pcsc_dword_t ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, pcsc_dword_t *buflen)
+{
+ long err;
+
+ err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
+ cntlbuf, len, buffer, buflen? *buflen:0, buflen);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ return 0;
+}
+
+
+static int
+close_pcsc_reader (int slot)
+{
+ (void)slot;
+
+ if (pcsc.context_valid)
+ {
+ pcsc_release_context (pcsc.context);
+ pcsc.context_valid = 0;
+ }
+ return 0;
+}
+
+
+/* Connect a PC/SC card. */
+static int
+connect_pcsc_card (int slot)
+{
+ long err;
+
+ log_assert (slot >= 0 && slot < MAX_READER);
+
+ if (reader_table[slot].pcsc.card)
+ return SW_HOST_ALREADY_CONNECTED;
+
+ reader_table[slot].atrlen = 0;
+ reader_table[slot].is_t0 = 0;
+
+ err = pcsc_connect (pcsc.context,
+ reader_table[slot].rdrname,
+ opt.pcsc_shared? PCSC_SHARE_SHARED:PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &reader_table[slot].pcsc.card,
+ &reader_table[slot].pcsc.protocol);
+ if (err)
+ {
+ reader_table[slot].pcsc.card = 0;
+ if (err != PCSC_E_NO_SMARTCARD)
+ log_error ("pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ if (err == PCSC_W_REMOVED_CARD && pcsc_cancel)
+ {
+ long err2;
+ err2 = pcsc_cancel (pcsc.context);
+ if (err2)
+ log_error ("pcsc_cancel failed: %s (0x%lx)\n",
+ pcsc_error_string (err2), err2);
+ else if (opt.verbose)
+ log_error ("pcsc_cancel succeeded\n");
+ }
+ }
+ else
+ {
+ char reader[250];
+ pcsc_dword_t readerlen, atrlen;
+ pcsc_dword_t card_state, card_protocol;
+
+ pcsc_vendor_specific_init (slot);
+
+ atrlen = DIM (reader_table[0].atr);
+ readerlen = sizeof reader - 1;
+ err = pcsc_status (reader_table[slot].pcsc.card,
+ reader, &readerlen,
+ &card_state, &card_protocol,
+ reader_table[slot].atr, &atrlen);
+ if (err)
+ log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+ pcsc_error_string (err), err, (long unsigned int)readerlen);
+ else
+ {
+ if (atrlen > DIM (reader_table[0].atr))
+ log_bug ("ATR returned by pcsc_status is too large\n");
+ reader_table[slot].atrlen = atrlen;
+ reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+ }
+ }
+
+ dump_reader_status (slot);
+ return pcsc_error_to_sw (err);
+}
+
+
+static int
+disconnect_pcsc_card (int slot)
+{
+ long err;
+
+ log_assert (slot >= 0 && slot < MAX_READER);
+
+ if (!reader_table[slot].pcsc.card)
+ return 0;
+
+ err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+ if (err)
+ {
+ log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return SW_HOST_CARD_IO_ERROR;
+ }
+ reader_table[slot].pcsc.card = 0;
+ return 0;
+}
+
+
+/* Send an PC/SC reset command and return a status word on error or 0
+ on success. */
+static int
+reset_pcsc_reader (int slot)
+{
+ int sw;
+
+ sw = disconnect_pcsc_card (slot);
+ if (!sw)
+ sw = connect_pcsc_card (slot);
+
+ return sw;
+}
+
+
+/* Examine reader specific parameters and initialize. This is mostly
+ for pinpad input. Called at opening the connection to the reader. */
+static int
+pcsc_vendor_specific_init (int slot)
+{
+ unsigned char buf[256];
+ pcsc_dword_t len;
+ int sw;
+ int vendor = 0;
+ int product = 0;
+ pcsc_dword_t get_tlv_ioctl = (pcsc_dword_t)-1;
+ unsigned char *p;
+
+ len = sizeof (buf);
+ sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
+ if (sw)
+ {
+ log_error ("pcsc_vendor_specific_init: GET_FEATURE_REQUEST failed: %d\n",
+ sw);
+ return SW_NOT_SUPPORTED;
+ }
+ else
+ {
+ p = buf;
+ while (p < buf + len)
+ {
+ unsigned char code = *p++;
+ int l = *p++;
+ unsigned int v = 0;
+
+ if (l == 1)
+ v = p[0];
+ else if (l == 2)
+ v = buf16_to_uint (p);
+ else if (l == 4)
+ v = buf32_to_uint (p);
+
+ if (code == FEATURE_VERIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.verify_ioctl = v;
+ else if (code == FEATURE_MODIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.modify_ioctl = v;
+ else if (code == FEATURE_GET_TLV_PROPERTIES)
+ get_tlv_ioctl = v;
+
+ if (DBG_CARD_IO)
+ log_debug ("feature: code=%02X, len=%d, v=%02X\n", code, l, v);
+
+ p += l;
+ }
+ }
+
+ if (get_tlv_ioctl == (pcsc_dword_t)-1)
+ {
+ /*
+ * For system which doesn't support GET_TLV_PROPERTIES,
+ * we put some heuristics here.
+ */
+ if (reader_table[slot].rdrname)
+ {
+ if (strstr (reader_table[slot].rdrname, "SPRx32"))
+ {
+ reader_table[slot].is_spr532 = 1;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if (strstr (reader_table[slot].rdrname, "ST-2xxx"))
+ {
+ reader_table[slot].pcsc.pinmax = 15;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if (strstr (reader_table[slot].rdrname, "cyberJack")
+ || strstr (reader_table[slot].rdrname, "DIGIPASS")
+ || strstr (reader_table[slot].rdrname, "Gnuk")
+ || strstr (reader_table[slot].rdrname, "KAAN")
+ || strstr (reader_table[slot].rdrname, "Trustica"))
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+
+ return 0;
+ }
+
+ len = sizeof (buf);
+ sw = control_pcsc (slot, get_tlv_ioctl, NULL, 0, buf, &len);
+ if (sw)
+ {
+ log_error ("pcsc_vendor_specific_init: GET_TLV_IOCTL failed: %d\n", sw);
+ return SW_NOT_SUPPORTED;
+ }
+
+ p = buf;
+ while (p < buf + len)
+ {
+ unsigned char tag = *p++;
+ int l = *p++;
+ unsigned int v = 0;
+
+ /* Umm... here is little endian, while the encoding above is big. */
+ if (l == 1)
+ v = p[0];
+ else if (l == 2)
+ v = (((unsigned int)p[1] << 8) | p[0]);
+ else if (l == 4)
+ v = (((unsigned int)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+
+ if (tag == PCSCv2_PART10_PROPERTY_bMinPINSize)
+ reader_table[slot].pcsc.pinmin = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_bMaxPINSize)
+ reader_table[slot].pcsc.pinmax = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_wIdVendor)
+ vendor = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_wIdProduct)
+ product = v;
+
+ if (DBG_CARD_IO)
+ log_debug ("TLV properties: tag=%02X, len=%d, v=%08X\n", tag, l, v);
+
+ p += l;
+ }
+
+ if (vendor == VENDOR_VEGA && product == VEGA_ALPHA)
+ {
+ /*
+ * Please read the comment of ccid_vendor_specific_init in
+ * ccid-driver.c.
+ */
+ const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
+ sw = control_pcsc (slot, CM_IOCTL_VENDOR_IFD_EXCHANGE,
+ cmd, sizeof (cmd), NULL, 0);
+ if (sw)
+ return SW_NOT_SUPPORTED;
+ }
+ else if (vendor == VENDOR_SCM && product == SCM_SPR532) /* SCM SPR532 */
+ {
+ reader_table[slot].is_spr532 = 1;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if (vendor == 0x046a)
+ {
+ /* Cherry ST-2xxx (product == 0x003e) supports TPDU level
+ * exchange. Other products which only support short APDU level
+ * exchange only work with shorter keys like RSA 1024.
+ */
+ reader_table[slot].pcsc.pinmax = 15;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if (vendor == 0x0c4b /* Tested with Reiner cyberJack GO */
+ || vendor == 0x1a44 /* Tested with Vasco DIGIPASS 920 */
+ || vendor == 0x234b /* Tested with FSIJ Gnuk Token */
+ || vendor == 0x0d46 /* Tested with KAAN Advanced??? */
+ || (vendor == 0x1fc9 && product == 0x81e6) /* Tested with Trustica Cryptoucan */)
+ reader_table[slot].pinpad_varlen_supported = 1;
+
+ return 0;
+}
+
+static int
+pcsc_init (void)
+{
+ static int pcsc_api_loaded;
+ long err;
+
+ /* Load the PC/SC API */
+ if (!pcsc_api_loaded)
+ {
+ void *handle;
+
+ handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
+ if (!handle)
+ {
+ log_error ("pscd_open_reader: failed to open driver '%s': %s\n",
+ opt.pcsc_driver, dlerror ());
+ return -1;
+ }
+
+ pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
+ pcsc_release_context = dlsym (handle, "SCardReleaseContext");
+ pcsc_cancel = dlsym (handle, "SCardCancel");
+ pcsc_list_readers = dlsym (handle, "SCardListReaders");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_list_readers)
+ pcsc_list_readers = dlsym (handle, "SCardListReadersA");
+#endif
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_get_status_change)
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
+#endif
+ pcsc_connect = dlsym (handle, "SCardConnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_connect)
+ pcsc_connect = dlsym (handle, "SCardConnectA");
+#endif
+ pcsc_reconnect = dlsym (handle, "SCardReconnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_reconnect)
+ pcsc_reconnect = dlsym (handle, "SCardReconnectA");
+#endif
+ pcsc_disconnect = dlsym (handle, "SCardDisconnect");
+ pcsc_status = dlsym (handle, "SCardStatus");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_status)
+ pcsc_status = dlsym (handle, "SCardStatusA");
+#endif
+ pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
+ pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
+ pcsc_transmit = dlsym (handle, "SCardTransmit");
+ pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
+
+ if (!pcsc_establish_context
+ || !pcsc_release_context
+ || !pcsc_list_readers
+ || !pcsc_get_status_change
+ || !pcsc_connect
+ || !pcsc_reconnect
+ || !pcsc_disconnect
+ || !pcsc_status
+ || !pcsc_begin_transaction
+ || !pcsc_end_transaction
+ || !pcsc_transmit
+ || !pcsc_control
+ /* || !pcsc_set_timeout */)
+ {
+ /* Note that set_timeout is currently not used and also not
+ available under Windows. */
+ log_error ("pcsc_open_reader: invalid PC/SC driver "
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ !!pcsc_establish_context,
+ !!pcsc_release_context,
+ !!pcsc_list_readers,
+ !!pcsc_get_status_change,
+ !!pcsc_connect,
+ !!pcsc_reconnect,
+ !!pcsc_disconnect,
+ !!pcsc_status,
+ !!pcsc_begin_transaction,
+ !!pcsc_end_transaction,
+ !!pcsc_transmit,
+ !!pcsc_set_timeout,
+ !!pcsc_control );
+ dlclose (handle);
+ return -1;
+ }
+ pcsc_api_loaded = 1;
+ }
+
+ pcsc.context_valid = 0;
+ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &pcsc.context);
+ if (err)
+ {
+ log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return -1;
+ }
+ pcsc.context_valid = 1;
+
+ return 0;
+}
+
+
+/* Select a reader from list of readers available. */
+static const char *
+select_a_reader (const char *list, unsigned int len)
+{
+ const char *black_list_to_skip[] = {
+ /* We do left match by strncmp(3). */
+ "Windows Hello"
+ };
+ const char *white_list_to_prefer[] = {
+ /* We do substring match by strstr(3). */
+ "SPRx32",
+ "Yubico"
+ };
+ const char *p = list;
+ const char *candidate = NULL;
+ unsigned int n;
+
+ /*
+ * (1) If one in the white list is found in LIST, that one is
+ * selected.
+ * (2) Otherwise, if one not in the black list is found in LIST,
+ * that is a candidate.
+ * (3) Select the first candidate, or in case of no candidate,
+ * return the first entry even if it's in the black list.
+ */
+ while (len)
+ {
+ int i;
+ int is_bad;
+
+ if (!*p)
+ break;
+
+ for (n=0; n < len; n++)
+ if (!p[n])
+ break;
+
+ /* Something wrong in the LIST. */
+ if (n >= len)
+ break;
+
+ for (i = 0; i < DIM (white_list_to_prefer); i++)
+ if (strstr (p, white_list_to_prefer[i]))
+ return p;
+
+ is_bad = 0;
+ for (i = 0; i < DIM (black_list_to_skip); i++)
+ if (!strncmp (p, black_list_to_skip[i],
+ strlen (black_list_to_skip[i])))
+ is_bad = 1;
+
+ if (!is_bad && !candidate)
+ candidate = p;
+
+ len -= n + 1;
+ p += n + 1;
+ }
+
+ if (candidate)
+ return candidate;
+
+ return list;
+}
+
+
+/* Open the PC/SC reader. If PORTSTR is NULL we default to a suitable
+ port. Returns -1 on error or a slot number for the reader. */
+static int
+open_pcsc_reader (const char *portstr)
+{
+ long err;
+ int slot;
+ char *list = NULL;
+ const char *rdrname = NULL;
+ pcsc_dword_t nreader = 0;
+ const char *p;
+ size_t n;
+ membuf_t reader_mb;
+
+ xfree (pcsc.reader_list);
+ pcsc.reader_list = NULL;
+
+ if (!pcsc.context_valid)
+ if (pcsc_init () < 0)
+ return -1;
+
+ if (DBG_READER)
+ log_debug ("open_pcsc_reader(portstr=%s)\n", portstr);
+
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1; /* No need to cleanup here. */
+
+ err = pcsc_list_readers (pcsc.context, NULL, NULL, &nreader);
+ if (!err)
+ {
+ list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
+ if (!list)
+ {
+ log_error ("error allocating memory for reader list\n");
+ close_pcsc_reader (slot);
+ reader_table[slot].used = 0;
+ unlock_slot (slot);
+ slot = -1 /*SW_HOST_OUT_OF_CORE*/;
+ goto leave;
+ }
+ err = pcsc_list_readers (pcsc.context, NULL, list, &nreader);
+ }
+ if (err)
+ {
+ log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ close_pcsc_reader (slot);
+ reader_table[slot].used = 0;
+ unlock_slot (slot);
+ xfree (list);
+ slot = -1;
+ goto leave;
+ }
+
+ init_membuf (&reader_mb, 256);
+
+ p = list;
+ while (nreader > 0)
+ {
+ if (!*p)
+ break;
+
+ for (n=0; n < nreader; n++)
+ if (!p[n])
+ break;
+
+ if (n >= nreader)
+ {
+ log_error ("invalid response from pcsc_list_readers\n");
+ xfree (get_membuf (&reader_mb, NULL));
+ close_pcsc_reader (slot);
+ reader_table[slot].used = 0;
+ unlock_slot (slot);
+ xfree (list);
+ slot = -1;
+ goto leave;
+ }
+
+ log_info ("detected reader '%s'\n", p);
+ put_membuf_str (&reader_mb, p);
+ put_membuf (&reader_mb, "\n", 1);
+ if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr)))
+ rdrname = p;
+ nreader -= n + 1;
+ p += n + 1;
+ }
+ put_membuf (&reader_mb, "", 1);
+ pcsc.reader_list = get_membuf (&reader_mb, NULL);
+ if (!pcsc.reader_list)
+ log_error ("error allocating memory for reader list\n");
+
+ if (!rdrname)
+ rdrname = select_a_reader (list, nreader);
+
+ reader_table[slot].rdrname = xtrystrdup (rdrname);
+ if (!reader_table[slot].rdrname)
+ {
+ log_error ("error allocating memory for reader name\n");
+ close_pcsc_reader (slot);
+ reader_table[slot].used = 0;
+ unlock_slot (slot);
+ slot = -1;
+ xfree (list);
+ goto leave;
+ }
+ xfree (list);
+ list = NULL;
+
+ reader_table[slot].pcsc.card = 0;
+ reader_table[slot].atrlen = 0;
+
+ reader_table[slot].connect_card = connect_pcsc_card;
+ reader_table[slot].disconnect_card = disconnect_pcsc_card;
+ reader_table[slot].close_reader = close_pcsc_reader;
+ reader_table[slot].reset_reader = reset_pcsc_reader;
+ reader_table[slot].get_status_reader = pcsc_get_status;
+ reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+ reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+ dump_reader_status (slot);
+ unlock_slot (slot);
+
+ leave:
+ if (DBG_READER)
+ log_debug ("open_pcsc_reader => slot=%d\n", slot);
+ return slot;
+}
+
+
+/* Check whether the reader supports the ISO command code COMMAND
+ on the pinpad. Return 0 on success. */
+static int
+check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+ int r;
+
+ if (reader_table[slot].pcsc.pinmin >= 0)
+ pininfo->minlen = reader_table[slot].pcsc.pinmin;
+
+ if (reader_table[slot].pcsc.pinmax >= 0)
+ pininfo->maxlen = reader_table[slot].pcsc.pinmax;
+
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 15;
+
+ if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
+ || (command == ISO7816_CHANGE_REFERENCE_DATA
+ && reader_table[slot].pcsc.modify_ioctl != 0))
+ r = 0; /* Success */
+ else
+ r = SW_NOT_SUPPORTED;
+
+ if (DBG_CARD_IO)
+ log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
+ (unsigned int)command, r);
+
+ if (reader_table[slot].pinpad_varlen_supported)
+ pininfo->fixedlen = 0;
+
+ return r;
+}
+
+#define PIN_VERIFY_STRUCTURE_SIZE 24
+static int
+pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ int sw;
+ unsigned char *pin_verify;
+ int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
+ /*
+ * The result buffer is only expected to have two-byte result on
+ * return. However, some implementation uses this buffer for lower
+ * layer too and it assumes that there is enough space for lower
+ * layer communication. Such an implementation fails for TPDU
+ * readers with "insufficient buffer", as it needs header and
+ * trailer. Six is the number for header + result + trailer (TPDU).
+ */
+ unsigned char result[6];
+ pcsc_dword_t resultlen = 6;
+ int no_lc;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return SW_NOT_SUPPORTED;
+
+ pin_verify = xtrymalloc (len);
+ if (!pin_verify)
+ return SW_HOST_OUT_OF_CORE;
+
+ no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
+
+ pin_verify[0] = 0x00; /* bTimeOut */
+ pin_verify[1] = 0x00; /* bTimeOut2 */
+ pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_verify[3] = pininfo->fixedlen; /* bmPINBlockString */
+ pin_verify[4] = 0x00; /* bmPINLengthFormat */
+ pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_verify[7] |= 0x01; /* Max size reached. */
+ pin_verify[8] = 0x01; /* bNumberMessage: One message */
+ pin_verify[9] = 0x09; /* wLangId: 0x0409: US English */
+ pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */
+ pin_verify[11] = 0x00; /* bMsgIndex */
+ pin_verify[12] = 0x00; /* bTeoPrologue[0] */
+ pin_verify[13] = 0x00; /* bTeoPrologue[1] */
+ pin_verify[14] = pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
+ pin_verify[15] = pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+ pin_verify[16] = 0x00; /* ulDataLength */
+ pin_verify[17] = 0x00; /* ulDataLength */
+ pin_verify[18] = 0x00; /* ulDataLength */
+ pin_verify[19] = class; /* abData[0] */
+ pin_verify[20] = ins; /* abData[1] */
+ pin_verify[21] = p0; /* abData[2] */
+ pin_verify[22] = p1; /* abData[3] */
+ pin_verify[23] = pininfo->fixedlen; /* abData[4] */
+ if (pininfo->fixedlen)
+ memset (&pin_verify[24], 0xff, pininfo->fixedlen);
+ else if (no_lc)
+ len--;
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
+ pin_verify, len, result, &resultlen);
+ xfree (pin_verify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
+
+
+#define PIN_MODIFY_STRUCTURE_SIZE 29
+static int
+pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ int sw;
+ unsigned char *pin_modify;
+ int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
+ unsigned char result[6]; /* See the comment at pinpad_verify. */
+ pcsc_dword_t resultlen = 6;
+ int no_lc;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return SW_NOT_SUPPORTED;
+
+ pin_modify = xtrymalloc (len);
+ if (!pin_modify)
+ return SW_HOST_OUT_OF_CORE;
+
+ no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
+
+ pin_modify[0] = 0x00; /* bTimeOut */
+ pin_modify[1] = 0x00; /* bTimeOut2 */
+ pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_modify[3] = pininfo->fixedlen; /* bmPINBlockString */
+ pin_modify[4] = 0x00; /* bmPINLengthFormat */
+ pin_modify[5] = 0x00; /* bInsertionOffsetOld */
+ pin_modify[6] = pininfo->fixedlen; /* bInsertionOffsetNew */
+ pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_modify[9] = (p0 == 0 ? 0x03 : 0x01);
+ /* bConfirmPIN
+ * 0x00: new PIN once
+ * 0x01: new PIN twice (confirmation)
+ * 0x02: old PIN and new PIN once
+ * 0x03: old PIN and new PIN twice (confirmation)
+ */
+ pin_modify[10] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_modify[10] |= 0x01; /* Max size reached. */
+ pin_modify[11] = 0x03; /* bNumberMessage: Three messages */
+ pin_modify[12] = 0x09; /* wLangId: 0x0409: US English */
+ pin_modify[13] = 0x04; /* wLangId: 0x0409: US English */
+ pin_modify[14] = 0x00; /* bMsgIndex1 */
+ pin_modify[15] = 0x01; /* bMsgIndex2 */
+ pin_modify[16] = 0x02; /* bMsgIndex3 */
+ pin_modify[17] = 0x00; /* bTeoPrologue[0] */
+ pin_modify[18] = 0x00; /* bTeoPrologue[1] */
+ pin_modify[19] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
+ pin_modify[20] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+ pin_modify[21] = 0x00; /* ulDataLength */
+ pin_modify[22] = 0x00; /* ulDataLength */
+ pin_modify[23] = 0x00; /* ulDataLength */
+ pin_modify[24] = class; /* abData[0] */
+ pin_modify[25] = ins; /* abData[1] */
+ pin_modify[26] = p0; /* abData[2] */
+ pin_modify[27] = p1; /* abData[3] */
+ pin_modify[28] = 2 * pininfo->fixedlen; /* abData[4] */
+ if (pininfo->fixedlen)
+ memset (&pin_modify[29], 0xff, 2 * pininfo->fixedlen);
+ else if (no_lc)
+ len--;
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, (int)pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
+ pin_modify, len, result, &resultlen);
+ xfree (pin_modify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
+
+#ifdef HAVE_LIBUSB
+/*
+ Internal CCID driver interface.
+ */
+
+
+static void
+dump_ccid_reader_status (int slot)
+{
+ log_info ("reader slot %d: using ccid driver\n", slot);
+}
+
+static int
+close_ccid_reader (int slot)
+{
+ ccid_close_reader (reader_table[slot].ccid.handle);
+ return 0;
+}
+
+
+static int
+reset_ccid_reader (int slot)
+{
+ int err;
+ reader_table_t slotp = reader_table + slot;
+ unsigned char atr[33];
+ size_t atrlen;
+
+ err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
+ if (err)
+ return err;
+ /* If the reset was successful, update the ATR. */
+ log_assert (sizeof slotp->atr >= sizeof atr);
+ slotp->atrlen = atrlen;
+ memcpy (slotp->atr, atr, atrlen);
+ dump_reader_status (slot);
+ return 0;
+}
+
+
+static int
+set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+ reader_table_t slotp = reader_table + slot;
+
+ return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
+}
+
+static int
+set_prompt_cb_ccid_reader (int slot, void (*cb) (void *, int ), void *cb_arg)
+{
+ reader_table_t slotp = reader_table + slot;
+
+ return ccid_set_prompt_cb (slotp->ccid.handle, cb, cb_arg);
+}
+
+
+static int
+get_status_ccid (int slot, unsigned int *status, int on_wire)
+{
+ int rc;
+ int bits;
+
+ rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits, on_wire);
+ if (rc)
+ return rc;
+
+ if (bits == 0)
+ *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
+ else if (bits == 1)
+ *status = APDU_CARD_PRESENT;
+ else
+ *status = 0;
+
+ return 0;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual returned size will be
+ set to BUFLEN. Returns: Internal CCID driver error code. */
+static int
+send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ pininfo_t *pininfo)
+{
+ long err;
+ size_t maxbuflen;
+
+ /* If we don't have an ATR, we need to reset the reader first. */
+ if (!reader_table[slot].atrlen
+ && (err = reset_ccid_reader (slot)))
+ return err;
+
+ if (DBG_CARD_IO)
+ log_printhex (apdu, apdulen, " raw apdu:");
+
+ maxbuflen = *buflen;
+ if (pininfo)
+ err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, apdulen, pininfo,
+ buffer, maxbuflen, buflen);
+ else
+ err = ccid_transceive (reader_table[slot].ccid.handle,
+ apdu, apdulen,
+ buffer, maxbuflen, buflen);
+ if (err)
+ log_error ("ccid_transceive failed: (0x%lx)\n",
+ err);
+
+ return err;
+}
+
+
+/* Check whether the CCID reader supports the ISO command code COMMAND
+ on the pinpad. Return 0 on success. For a description of the pin
+ parameters, see ccid-driver.c */
+static int
+check_ccid_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+ unsigned char apdu[] = { 0, 0, 0, 0x81 };
+
+ apdu[1] = command;
+ return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu,
+ sizeof apdu, pininfo, NULL, 0, NULL);
+}
+
+
+static int
+ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ unsigned char apdu[4];
+ int err, sw;
+ unsigned char result[2];
+ size_t resultlen = 2;
+
+ apdu[0] = class;
+ apdu[1] = ins;
+ apdu[2] = p0;
+ apdu[3] = p1;
+ err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, sizeof apdu, pininfo,
+ result, 2, &resultlen);
+ if (err)
+ return err;
+
+ if (resultlen < 2)
+ return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ return sw;
+}
+
+
+/* Open the reader and try to read an ATR. */
+static int
+open_ccid_reader (struct dev_list *dl)
+{
+ int err;
+ int slot;
+ int require_get_status;
+ reader_table_t slotp;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+ slotp = reader_table + slot;
+
+ err = ccid_open_reader (dl->portstr, dl->idx, dl->table,
+ &slotp->ccid.handle, &slotp->rdrname);
+ if (!err)
+ {
+ err = ccid_get_atr (slotp->ccid.handle,
+ slotp->atr, sizeof slotp->atr, &slotp->atrlen);
+ if (err)
+ ccid_close_reader (slotp->ccid.handle);
+ }
+
+ if (err)
+ {
+ slotp->used = 0;
+ unlock_slot (slot);
+ return -1;
+ }
+
+ require_get_status = ccid_require_get_status (slotp->ccid.handle);
+
+ reader_table[slot].close_reader = close_ccid_reader;
+ reader_table[slot].reset_reader = reset_ccid_reader;
+ reader_table[slot].get_status_reader = get_status_ccid;
+ reader_table[slot].send_apdu_reader = send_apdu_ccid;
+ reader_table[slot].check_pinpad = check_ccid_pinpad;
+ reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+ reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+ reader_table[slot].set_prompt_cb = set_prompt_cb_ccid_reader;
+ reader_table[slot].pinpad_verify = ccid_pinpad_operation;
+ reader_table[slot].pinpad_modify = ccid_pinpad_operation;
+ /* Our CCID reader code does not support T=0 at all, thus reset the
+ flag. */
+ reader_table[slot].is_t0 = 0;
+ reader_table[slot].require_get_status = require_get_status;
+
+ dump_reader_status (slot);
+ unlock_slot (slot);
+ return slot;
+}
+#endif /* HAVE_LIBUSB */
+
+#ifdef USE_G10CODE_RAPDU
+/*
+ The Remote APDU Interface.
+
+ This uses the Remote APDU protocol to contact a reader.
+
+ The port number is actually an index into the list of ports as
+ returned via the protocol.
+ */
+
+
+static int
+rapdu_status_to_sw (int status)
+{
+ int rc;
+
+ switch (status)
+ {
+ case RAPDU_STATUS_SUCCESS: rc = 0; break;
+
+ case RAPDU_STATUS_INVCMD:
+ case RAPDU_STATUS_INVPROT:
+ case RAPDU_STATUS_INVSEQ:
+ case RAPDU_STATUS_INVCOOKIE:
+ case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break;
+
+ case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
+ case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break;
+ case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break;
+ case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break;
+ case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break;
+ case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
+
+ default: rc = SW_HOST_GENERAL_ERROR; break;
+ }
+
+ return rc;
+}
+
+
+
+static int
+close_rapdu_reader (int slot)
+{
+ rapdu_release (reader_table[slot].rapdu.handle);
+ return 0;
+}
+
+
+static int
+reset_rapdu_reader (int slot)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+
+ slotp = reader_table + slot;
+
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+ if (err)
+ {
+ log_error ("sending rapdu command RESET failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command RESET failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+ if (msg->datalen > DIM (slotp->atr))
+ {
+ log_error ("ATR returned by the RAPDU layer is too large\n");
+ rapdu_msg_release (msg);
+ return SW_HOST_INV_VALUE;
+ }
+ slotp->atrlen = msg->datalen;
+ memcpy (slotp->atr, msg->data, msg->datalen);
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+
+static int
+my_rapdu_get_status (int slot, unsigned int *status, int on_wire)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+ int oldslot;
+
+ (void)on_wire;
+ slotp = reader_table + slot;
+
+ oldslot = rapdu_set_reader (slotp->rapdu.handle, slot);
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS);
+ rapdu_set_reader (slotp->rapdu.handle, oldslot);
+ if (err)
+ {
+ log_error ("sending rapdu command GET_STATUS failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command GET_STATUS failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+ *status = msg->data[0];
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual returned size will be
+ set to BUFLEN. Returns: APDU error code. */
+static int
+my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ pininfo_t *pininfo)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+ size_t maxlen = *buflen;
+
+ slotp = reader_table + slot;
+
+ *buflen = 0;
+ if (DBG_CARD_IO)
+ log_printhex (apdu, apdulen, " APDU_data:");
+
+ if (apdulen < 4)
+ {
+ log_error ("rapdu_send_apdu: APDU is too short\n");
+ return SW_HOST_INV_VALUE;
+ }
+
+ err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
+ if (err)
+ {
+ log_error ("sending rapdu command APDU failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command APDU failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+
+ if (msg->datalen > maxlen)
+ {
+ log_error ("rapdu response apdu too large\n");
+ rapdu_msg_release (msg);
+ return SW_HOST_INV_VALUE;
+ }
+
+ *buflen = msg->datalen;
+ memcpy (buffer, msg->data, msg->datalen);
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+static int
+open_rapdu_reader (int portno,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value)
+{
+ int err;
+ int slot;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+ slotp = reader_table + slot;
+
+ slotp->rapdu.handle = rapdu_new ();
+ if (!slotp->rapdu.handle)
+ {
+ slotp->used = 0;
+ unlock_slot (slot);
+ return -1;
+ }
+
+ rapdu_set_reader (slotp->rapdu.handle, portno);
+
+ rapdu_set_iofunc (slotp->rapdu.handle,
+ readfnc, readfnc_value,
+ writefnc, writefnc_value,
+ closefnc, closefnc_value);
+ rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
+
+ /* First try to get the current ATR, but if the card is inactive
+ issue a reset instead. */
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
+ if (err == RAPDU_STATUS_NEEDRESET)
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+ if (err)
+ {
+ log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ goto failure;
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_info ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ goto failure;
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ log_info ("rapdu command GET ATR failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ goto failure;
+ }
+ if (msg->datalen > DIM (slotp->atr))
+ {
+ log_error ("ATR returned by the RAPDU layer is too large\n");
+ goto failure;
+ }
+ slotp->atrlen = msg->datalen;
+ memcpy (slotp->atr, msg->data, msg->datalen);
+
+ reader_table[slot].close_reader = close_rapdu_reader;
+ reader_table[slot].reset_reader = reset_rapdu_reader;
+ reader_table[slot].get_status_reader = my_rapdu_get_status;
+ reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
+ reader_table[slot].check_pinpad = NULL;
+ reader_table[slot].dump_status_reader = NULL;
+ reader_table[slot].pinpad_verify = NULL;
+ reader_table[slot].pinpad_modify = NULL;
+
+ dump_reader_status (slot);
+ rapdu_msg_release (msg);
+ unlock_slot (slot);
+ return slot;
+
+ failure:
+ rapdu_msg_release (msg);
+ rapdu_release (slotp->rapdu.handle);
+ slotp->used = 0;
+ unlock_slot (slot);
+ return -1;
+}
+
+#endif /*USE_G10CODE_RAPDU*/
+
+
+
+/*
+ Driver Access
+ */
+gpg_error_t
+apdu_dev_list_start (const char *portstr, struct dev_list **l_p)
+{
+ struct dev_list *dl = xtrymalloc (sizeof (struct dev_list));
+
+ *l_p = NULL;
+ if (!dl)
+ return gpg_error_from_syserror ();
+
+ dl->portstr = portstr;
+ dl->idx = 0;
+
+ npth_mutex_lock (&reader_table_lock);
+
+#ifdef HAVE_LIBUSB
+ if (opt.disable_ccid)
+ {
+ dl->table = NULL;
+ dl->idx_max = 1;
+ }
+ else
+ {
+ gpg_error_t err;
+
+ err = ccid_dev_scan (&dl->idx_max, &dl->table);
+ if (err)
+ return err;
+
+ if (dl->idx_max == 0)
+ {
+ /* If a CCID reader specification has been given, the user does
+ not want a fallback to other drivers. */
+ if (portstr && strlen (portstr) > 5 && portstr[4] == ':')
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
+
+ xfree (dl);
+ npth_mutex_unlock (&reader_table_lock);
+ return gpg_error (GPG_ERR_ENODEV);
+ }
+ else
+ dl->idx_max = 1;
+ }
+ }
+#else
+ dl->table = NULL;
+ dl->idx_max = 1;
+#endif /* HAVE_LIBUSB */
+
+ *l_p = dl;
+ return 0;
+}
+
+void
+apdu_dev_list_finish (struct dev_list *dl)
+{
+#ifdef HAVE_LIBUSB
+ if (dl->table)
+ ccid_dev_scan_finish (dl->table, dl->idx_max);
+#endif
+ xfree (dl);
+ npth_mutex_unlock (&reader_table_lock);
+}
+
+
+int
+apdu_open_reader (struct dev_list *dl, int app_empty)
+{
+ int slot;
+
+#ifdef HAVE_LIBUSB
+ if (dl->table)
+ { /* CCID readers. */
+ int readerno;
+
+ /* See whether we want to use the reader ID string or a reader
+ number. A readerno of -1 indicates that the reader ID string is
+ to be used. */
+ if (dl->portstr && strchr (dl->portstr, ':'))
+ readerno = -1; /* We want to use the readerid. */
+ else if (dl->portstr)
+ {
+ readerno = atoi (dl->portstr);
+ if (readerno < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ readerno = 0; /* Default. */
+
+ if (readerno > 0)
+ { /* Use single, the specific reader. */
+ if (readerno >= dl->idx_max)
+ return -1;
+
+ dl->idx = readerno;
+ dl->portstr = NULL;
+ slot = open_ccid_reader (dl);
+ dl->idx = dl->idx_max;
+ if (slot >= 0)
+ return slot;
+ else
+ return -1;
+ }
+
+ while (dl->idx < dl->idx_max)
+ {
+ unsigned int bai = ccid_get_BAI (dl->idx, dl->table);
+
+ if (DBG_READER)
+ log_debug ("apdu_open_reader: BAI=%x\n", bai);
+
+ /* Check identity by BAI against already opened HANDLEs. */
+ for (slot = 0; slot < MAX_READER; slot++)
+ if (reader_table[slot].used
+ && reader_table[slot].ccid.handle
+ && ccid_compare_BAI (reader_table[slot].ccid.handle, bai))
+ break;
+
+ if (slot == MAX_READER)
+ { /* Found a new device. */
+ if (DBG_READER)
+ log_debug ("apdu_open_reader: new device=%x\n", bai);
+
+ slot = open_ccid_reader (dl);
+
+ dl->idx++;
+ if (slot >= 0)
+ return slot;
+ else
+ {
+ /* Skip this reader. */
+ log_error ("ccid open error: skip\n");
+ continue;
+ }
+ }
+ else
+ dl->idx++;
+ }
+
+ /* Not found. Try one for PC/SC, only when it's the initial scan. */
+ if (app_empty && dl->idx == dl->idx_max)
+ {
+ dl->idx++;
+ slot = open_pcsc_reader (dl->portstr);
+ }
+ else
+ slot = -1;
+ }
+ else
+#endif
+ { /* PC/SC readers. */
+
+ if (app_empty && dl->idx == 0)
+ {
+ dl->idx++;
+ slot = open_pcsc_reader (dl->portstr);
+ }
+ else
+ slot = -1;
+ }
+
+ return slot;
+}
+
+
+/* Open an remote reader and return an internal slot number or -1 on
+ error. This function is an alternative to apdu_open_reader and used
+ with remote readers only. Note that the supplied CLOSEFNC will
+ only be called once and the slot will not be valid afther this.
+
+ If PORTSTR is NULL we default to the first available port.
+*/
+int
+apdu_open_remote_reader (const char *portstr,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value)
+{
+#ifdef USE_G10CODE_RAPDU
+ return open_rapdu_reader (portstr? atoi (portstr) : 0,
+ cookie, length,
+ readfnc, readfnc_value,
+ writefnc, writefnc_value,
+ closefnc, closefnc_value);
+#else
+ (void)portstr;
+ (void)cookie;
+ (void)length;
+ (void)readfnc;
+ (void)readfnc_value;
+ (void)writefnc;
+ (void)writefnc_value;
+ (void)closefnc;
+ (void)closefnc_value;
+#ifdef _WIN32
+ errno = ENOENT;
+#else
+ errno = ENOSYS;
+#endif
+ return -1;
+#endif
+}
+
+
+int
+apdu_close_reader (int slot)
+{
+ int sw;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_close_reader: slot=%d\n", slot);
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
+ sw = apdu_disconnect (slot);
+ if (sw)
+ {
+ /*
+ * When the reader/token was removed it might come here.
+ * It should go through to call CLOSE_READER even if we got an error.
+ */
+ if (DBG_READER)
+ log_debug ("apdu_close_reader => 0x%x (apdu_disconnect)\n", sw);
+ }
+ if (reader_table[slot].close_reader)
+ {
+ sw = reader_table[slot].close_reader (slot);
+ xfree (reader_table[slot].rdrname);
+ reader_table[slot].rdrname = NULL;
+ reader_table[slot].used = 0;
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => 0x%x (close_reader)\n", sw);
+ return sw;
+ }
+ xfree (reader_table[slot].rdrname);
+ reader_table[slot].rdrname = NULL;
+ reader_table[slot].used = 0;
+ if (DBG_READER)
+ log_debug ("leave: apdu_close_reader => SW_HOST_NOT_SUPPORTED\n");
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Function suitable for a cleanup function to close all reader. It
+ should not be used if the reader will be opened again. The reason
+ for implementing this to properly close USB devices so that they
+ will startup the next time without error. */
+void
+apdu_prepare_exit (void)
+{
+ static int sentinel;
+ int slot;
+
+ if (!sentinel)
+ {
+ sentinel = 1;
+ npth_mutex_lock (&reader_table_lock);
+ for (slot = 0; slot < MAX_READER; slot++)
+ if (reader_table[slot].used)
+ {
+ apdu_disconnect (slot);
+ if (reader_table[slot].close_reader)
+ reader_table[slot].close_reader (slot);
+ xfree (reader_table[slot].rdrname);
+ reader_table[slot].rdrname = NULL;
+ reader_table[slot].used = 0;
+ }
+ npth_mutex_unlock (&reader_table_lock);
+ sentinel = 0;
+ }
+}
+
+
+/* Enumerate all readers and return information on whether this reader
+ is in use. The caller should start with SLOT set to 0 and
+ increment it with each call until an error is returned. */
+int
+apdu_enum_reader (int slot, int *used)
+{
+ if (slot < 0 || slot >= MAX_READER)
+ return SW_HOST_NO_DRIVER;
+ *used = reader_table[slot].used;
+ return 0;
+}
+
+
+/* Connect a card. This is used to power up the card and make sure
+ that an ATR is available. Depending on the reader backend it may
+ return an error for an inactive card or if no card is available.
+ Return -1 on error. Return 1 if reader requires get_status to
+ watch card removal. Return 0 if it's a token (always with a card),
+ or it supports INTERRUPT endpoint to watch card removal.
+ */
+int
+apdu_connect (int slot)
+{
+ int sw = 0;
+ unsigned int status = 0;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_connect: slot=%d\n", slot);
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_connect => SW_HOST_NO_DRIVER\n");
+ return -1;
+ }
+
+ /* Only if the access method provides a connect function we use it.
+ If not, we expect that the card has been implicitly connected by
+ apdu_open_reader. */
+ if (reader_table[slot].connect_card)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].connect_card (slot);
+ unlock_slot (slot);
+ }
+ }
+
+ /* We need to call apdu_get_status_internal, so that the last-status
+ machinery gets setup properly even if a card is inserted while
+ scdaemon is fired up and apdu_get_status has not yet been called.
+ Without that we would force a reset of the card with the next
+ call to apdu_get_status. */
+ if (!sw)
+ sw = apdu_get_status_internal (slot, 1, &status, 1);
+
+ if (sw)
+ ;
+ else if (!(status & APDU_CARD_PRESENT))
+ sw = SW_HOST_NO_CARD;
+ else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
+ sw = SW_HOST_CARD_INACTIVE;
+
+ if (sw == SW_HOST_CARD_INACTIVE)
+ {
+ /* Try power it up again. */
+ sw = apdu_reset (slot);
+ }
+
+ if (DBG_READER)
+ log_debug ("leave: apdu_connect => sw=0x%x\n", sw);
+
+ if (sw)
+ return -1;
+
+ return reader_table[slot].require_get_status;
+}
+
+
+int
+apdu_disconnect (int slot)
+{
+ int sw;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_disconnect: slot=%d\n", slot);
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
+
+ if (reader_table[slot].disconnect_card)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].disconnect_card (slot);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+
+ if (DBG_READER)
+ log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
+ return sw;
+}
+
+
+/* Set the progress callback of SLOT to CB and its args to CB_ARG. If
+ CB is NULL the progress callback is removed. */
+int
+apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].set_progress_cb)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].set_progress_cb (slot, cb, cb_arg);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+ return sw;
+}
+
+
+int
+apdu_set_prompt_cb (int slot, void (*cb) (void *, int), void *cb_arg)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].set_prompt_cb)
+ {
+ sw = lock_slot (slot);
+ if (!sw)
+ {
+ sw = reader_table[slot].set_prompt_cb (slot, cb, cb_arg);
+ unlock_slot (slot);
+ }
+ }
+ else
+ sw = 0;
+ return sw;
+}
+
+
+/* Do a reset for the card in reader at SLOT. */
+int
+apdu_reset (int slot)
+{
+ int sw;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_reset: slot=%d\n", slot);
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
+ return SW_HOST_NO_DRIVER;
+ }
+
+ if ((sw = lock_slot (slot)))
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
+ return sw;
+ }
+
+ if (reader_table[slot].reset_reader)
+ sw = reader_table[slot].reset_reader (slot);
+
+ unlock_slot (slot);
+ if (DBG_READER)
+ log_debug ("leave: apdu_reset => sw=0x%x\n", sw);
+ return sw;
+}
+
+
+/* Return the ATR or NULL if none is available. On success the length
+ of the ATR is stored at ATRLEN. The caller must free the returned
+ value. */
+unsigned char *
+apdu_get_atr (int slot, size_t *atrlen)
+{
+ unsigned char *buf;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_get_atr: slot=%d\n", slot);
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
+ return NULL;
+ }
+ if (!reader_table[slot].atrlen)
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
+ return NULL;
+ }
+
+ buf = xtrymalloc (reader_table[slot].atrlen);
+ if (!buf)
+ {
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
+ return NULL;
+ }
+ memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
+ *atrlen = reader_table[slot].atrlen;
+ if (DBG_READER)
+ log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
+ return buf;
+}
+
+
+
+/* Retrieve the status for SLOT. The function does only wait for the
+ card to become available if HANG is set to true. On success the
+ bits in STATUS will be set to
+
+ APDU_CARD_USABLE (bit 0) = card present and usable
+ APDU_CARD_PRESENT (bit 1) = card present
+ APDU_CARD_ACTIVE (bit 2) = card active
+ (bit 3) = card access locked [not yet implemented]
+
+ For most applications, testing bit 0 is sufficient.
+*/
+static int
+apdu_get_status_internal (int slot, int hang, unsigned int *status, int on_wire)
+{
+ int sw;
+ unsigned int s = 0;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
+ return sw;
+
+ if (reader_table[slot].get_status_reader)
+ sw = reader_table[slot].get_status_reader (slot, &s, on_wire);
+
+ unlock_slot (slot);
+
+ if (sw)
+ {
+ if (on_wire)
+ reader_table[slot].atrlen = 0;
+ s = 0;
+ }
+
+ if (status)
+ *status = s;
+ return sw;
+}
+
+
+/* See above for a description. */
+int
+apdu_get_status (int slot, int hang, unsigned int *status)
+{
+ int sw;
+
+ if (DBG_READER)
+ log_debug ("enter: apdu_get_status: slot=%d hang=%d\n", slot, hang);
+ sw = apdu_get_status_internal (slot, hang, status, 0);
+ if (DBG_READER)
+ {
+ if (status)
+ log_debug ("leave: apdu_get_status => sw=0x%x status=%u\n",
+ sw, *status);
+ else
+ log_debug ("leave: apdu_get_status => sw=0x%x\n", sw);
+ }
+ return sw;
+}
+
+
+/* Check whether the reader supports the ISO command code COMMAND on
+ the pinpad. Return 0 on success. For a description of the pin
+ parameters, see ccid-driver.c */
+int
+apdu_check_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (opt.enable_pinpad_varlen)
+ pininfo->fixedlen = 0;
+
+ if (reader_table[slot].check_pinpad)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].check_pinpad (slot, command, pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].pinpad_verify)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].pinpad_verify (slot, class, ins, p0, p1,
+ pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].pinpad_modify)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].pinpad_modify (slot, class, ins, p0, p1,
+ pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Dispatcher for the actual send_apdu function. Note, that this
+ function should be called in locked state. */
+static int
+send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].send_apdu_reader)
+ return reader_table[slot].send_apdu_reader (slot,
+ apdu, apdulen,
+ buffer, buflen,
+ pininfo);
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Core APDU tranceiver function. Parameters are described at
+ apdu_send_le with the exception of PININFO which indicates pinpad
+ related operations if not NULL. If EXTENDED_MODE is not 0
+ command chaining or extended length will be used according to these
+ values:
+ n < 0 := Use command chaining with the data part limited to -n
+ in each chunk. If -1 is used a default value is used.
+ n == 0 := No extended mode or command chaining.
+ n == 1 := Use extended length for input and output without a
+ length limit.
+ n > 1 := Use extended length with up to N bytes.
+
+*/
+static int
+send_le (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen,
+ pininfo_t *pininfo, int extended_mode)
+{
+#define SHORT_RESULT_BUFFER_SIZE 258
+ /* We allocate 8 extra bytes as a safety margin towards a driver bug. */
+ unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+ unsigned char *result_buffer = NULL;
+ size_t result_buffer_size;
+ unsigned char *result;
+ size_t resultlen;
+ unsigned char short_apdu_buffer[5+256+1];
+ unsigned char *apdu_buffer = NULL;
+ size_t apdu_buffer_size;
+ unsigned char *apdu;
+ size_t apdulen;
+ int sw;
+ long rc; /* We need a long here due to PC/SC. */
+ int did_exact_length_hack = 0;
+ int use_chaining = 0;
+ int use_extended_length = 0;
+ int lc_chunk;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (DBG_CARD_IO)
+ log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
+ class, ins, p0, p1, lc, le, extended_mode);
+
+ if (lc != -1 && (lc > 255 || lc < 0))
+ {
+ /* Data does not fit into an APDU. What we do now depends on
+ the EXTENDED_MODE parameter. */
+ if (!extended_mode)
+ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
+ else if (extended_mode > 0)
+ use_extended_length = 1;
+ else if (extended_mode < 0)
+ {
+ /* Send APDU using chaining mode. */
+ if (lc > 16384)
+ return SW_WRONG_LENGTH; /* Sanity check. */
+ if ((class&0xf0) != 0)
+ return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */
+ use_chaining = extended_mode == -1? 255 : -extended_mode;
+ use_chaining &= 0xff;
+ }
+ else
+ return SW_HOST_INV_VALUE;
+ }
+ else if (lc == -1 && extended_mode > 0)
+ use_extended_length = 1;
+
+ if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
+ {
+ /* Expected Data does not fit into an APDU. What we do now
+ depends on the EXTENDED_MODE parameter. Note that a check
+ for command chaining does not make sense because we are
+ looking at Le. */
+ if (!extended_mode)
+ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
+ else if (use_extended_length)
+ ; /* We are already using extended length. */
+ else if (extended_mode > 0)
+ use_extended_length = 1;
+ else
+ return SW_HOST_INV_VALUE;
+ }
+
+ if ((!data && lc != -1) || (data && lc == -1))
+ return SW_HOST_INV_VALUE;
+
+ if (use_extended_length)
+ {
+ if (reader_table[slot].is_t0)
+ return SW_HOST_NOT_SUPPORTED;
+
+ /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le. */
+ apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
+ apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
+ if (!apdu_buffer)
+ return SW_HOST_OUT_OF_CORE;
+ apdu = apdu_buffer;
+ }
+ else
+ {
+ apdu_buffer_size = sizeof short_apdu_buffer;
+ apdu = short_apdu_buffer;
+ }
+
+ if (use_extended_length && (le > 256 || le < 0))
+ {
+ /* Two more bytes are needed for status bytes. */
+ result_buffer_size = le < 0? 4096 : (le + 2);
+ result_buffer = xtrymalloc (result_buffer_size);
+ if (!result_buffer)
+ {
+ xfree (apdu_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ result = result_buffer;
+ }
+ else
+ {
+ result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+ result = short_result_buffer;
+ }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+ if ((sw = lock_slot (slot)))
+ {
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return sw;
+ }
+
+ do
+ {
+ if (use_extended_length)
+ {
+ use_chaining = 0;
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = ins;
+ apdu[apdulen++] = p0;
+ apdu[apdulen++] = p1;
+ if (lc > 0)
+ {
+ apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
+ apdu[apdulen++] = ((lc >> 8) & 0xff);
+ apdu[apdulen++] = (lc & 0xff);
+ memcpy (apdu+apdulen, data, lc);
+ data += lc;
+ apdulen += lc;
+ }
+ if (le != -1)
+ {
+ if (lc <= 0)
+ apdu[apdulen++] = 0; /* Z byte: Extended length marker. */
+ apdu[apdulen++] = ((le >> 8) & 0xff);
+ apdu[apdulen++] = (le & 0xff);
+ }
+ }
+ else
+ {
+ apdulen = 0;
+ apdu[apdulen] = class;
+ if (use_chaining && lc > 255)
+ {
+ apdu[apdulen] |= 0x10;
+ log_assert (use_chaining < 256);
+ lc_chunk = use_chaining;
+ lc -= use_chaining;
+ }
+ else
+ {
+ use_chaining = 0;
+ lc_chunk = lc;
+ }
+ apdulen++;
+ apdu[apdulen++] = ins;
+ apdu[apdulen++] = p0;
+ apdu[apdulen++] = p1;
+ if (lc_chunk != -1)
+ {
+ apdu[apdulen++] = lc_chunk;
+ memcpy (apdu+apdulen, data, lc_chunk);
+ data += lc_chunk;
+ apdulen += lc_chunk;
+ /* T=0 does not allow the use of Lc together with Le;
+ thus disable Le in this case. */
+ if (reader_table[slot].is_t0)
+ le = -1;
+ }
+ if (le != -1 && !use_chaining)
+ apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
+ }
+
+ exact_length_hack:
+ /* As a safeguard don't pass any garbage to the driver. */
+ log_assert (apdulen <= apdu_buffer_size);
+ memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+ resultlen = result_buffer_size;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
+ if (rc || resultlen < 2)
+ {
+ log_info ("apdu_send_simple(%d) failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (!use_extended_length
+ && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
+ {
+ apdu[apdulen-1] = (sw & 0x00ff);
+ did_exact_length_hack = 1;
+ goto exact_length_hack;
+ }
+ }
+ while (use_chaining && sw == SW_SUCCESS);
+
+ if (apdu_buffer)
+ {
+ xfree (apdu_buffer);
+ apdu_buffer = NULL;
+ }
+
+ /* Store away the returned data but strip the statusword. */
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" response: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+ {
+ if (all_zero_p (result, resultlen))
+ log_debug (" dump: [all zero]\n");
+ else
+ log_printhex (result, resultlen, " dump:");
+ }
+ }
+
+ if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
+ {
+ if (retbuf)
+ {
+ *retbuf = xtrymalloc (resultlen? resultlen : 1);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ *retbuflen = resultlen;
+ memcpy (*retbuf, result, resultlen);
+ }
+ }
+ else if ((sw & 0xff00) == SW_MORE_DATA)
+ {
+ unsigned char *p = NULL, *tmp;
+ size_t bufsize = 4096;
+
+ /* It is likely that we need to return much more data, so we
+ start off with a large buffer. */
+ if (retbuf)
+ {
+ *retbuf = p = xtrymalloc (bufsize);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ log_assert (resultlen < bufsize);
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+
+ do
+ {
+ int len = (sw & 0x00ff);
+
+ if (DBG_CARD_IO)
+ log_debug ("apdu_send_simple(%d): %d more bytes available\n",
+ slot, len);
+ apdu_buffer_size = sizeof short_apdu_buffer;
+ apdu = short_apdu_buffer;
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = 0xC0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = len;
+ log_assert (apdulen <= apdu_buffer_size);
+ memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
+ resultlen = result_buffer_size;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_simple(%d) for get response failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" more: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+ {
+ if (all_zero_p (result, resultlen))
+ log_debug ( " dump: [all zero]\n");
+ else
+ log_printhex (result, resultlen, " dump:");
+ }
+ }
+
+ if ((sw & 0xff00) == SW_MORE_DATA
+ || sw == SW_SUCCESS
+ || sw == SW_EOF_REACHED )
+ {
+ if (retbuf && resultlen)
+ {
+ if (p - *retbuf + resultlen > bufsize)
+ {
+ bufsize += resultlen > 4096? resultlen: 4096;
+ tmp = xtryrealloc (*retbuf, bufsize);
+ if (!tmp)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ p = tmp + (p - *retbuf);
+ *retbuf = tmp;
+ }
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+ }
+ else
+ log_info ("apdu_send_simple(%d) "
+ "got unexpected status %04X from get response\n",
+ slot, sw);
+ }
+ while ((sw & 0xff00) == SW_MORE_DATA);
+
+ if (retbuf)
+ {
+ *retbuflen = p - *retbuf;
+ tmp = xtryrealloc (*retbuf, *retbuflen);
+ if (tmp)
+ *retbuf = tmp;
+ }
+ }
+
+ unlock_slot (slot);
+ xfree (result_buffer);
+
+ if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
+ {
+ if (all_zero_p (*retbuf, *retbuflen))
+ log_debug (" dump: [all zero]\n");
+ else
+ log_printhex (*retbuf, *retbuflen, " dump:");
+ }
+
+ return sw;
+}
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
+ for LC won't sent this field and the data field; in this case DATA
+ must also be passed as NULL. If EXTENDED_MODE is not 0 command
+ chaining or extended length will be used; see send_le for details.
+ The return value is the status word or -1 for an invalid SLOT or
+ other non card related error. If RETBUF is not NULL, it will
+ receive an allocated buffer with the returned data. The length of
+ that data will be put into *RETBUFLEN. The caller is responsible
+ for releasing the buffer even in case of errors. */
+int
+apdu_send_le(int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen)
+{
+ return send_le (slot, class, ins, p0, p1,
+ lc, data, le,
+ retbuf, retbuflen,
+ NULL, extended_mode);
+}
+
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
+ LC won't sent this field and the data field; in this case DATA must
+ also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
+ or extended length will be used; see send_le for details. The
+ return value is the status word or -1 for an invalid SLOT or other
+ non card related error. If RETBUF is not NULL, it will receive an
+ allocated buffer with the returned data. The length of that data
+ will be put into *RETBUFLEN. The caller is responsible for
+ releasing the buffer even in case of errors. */
+int
+apdu_send (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
+{
+ return send_le (slot, class, ins, p0, p1, lc, data, 256,
+ retbuf, retbuflen, NULL, extended_mode);
+}
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
+ LC won't sent this field and the data field; in this case DATA must
+ also be passed as NULL. If EXTENDED_MODE is not 0 command chaining
+ or extended length will be used; see send_le for details. The
+ return value is the status word or -1 for an invalid SLOT or other
+ non card related error. No data will be returned. */
+int
+apdu_send_simple (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const char *data)
+{
+ return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
+ extended_mode);
+}
+
+
+/* This is a more generic version of the apdu sending routine. It
+ * takes an already formatted APDU in APDUDATA or length APDUDATALEN
+ * and returns with an APDU including the status word. With
+ * HANDLE_MORE set to true this function will handle the MORE DATA
+ * status and return all APDUs concatenated with one status word at
+ * the end. If EXTENDED_LENGTH is != 0 extended lengths are allowed
+ * with a max. result data length of EXTENDED_LENGTH bytes. The
+ * function does not return a regular status word but 0 on success.
+ * If the slot is locked, the function returns immediately with an
+ * error.
+ *
+ * Out of historical reasons the function returns 0 on success and
+ * outs the status word at the end of the result to be able to get the
+ * status word in the case of a not provided RETBUF, R_SW can be used
+ * to store the SW. But note that R_SW qill only be set if the
+ * function returns 0. */
+int
+apdu_send_direct (int slot, size_t extended_length,
+ const unsigned char *apdudata, size_t apdudatalen,
+ int handle_more, unsigned int *r_sw,
+ unsigned char **retbuf, size_t *retbuflen)
+{
+#define SHORT_RESULT_BUFFER_SIZE 258
+ unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
+ unsigned char *result_buffer = NULL;
+ size_t result_buffer_size;
+ unsigned char *result;
+ size_t resultlen;
+ unsigned char short_apdu_buffer[5+256+10];
+ unsigned char *apdu_buffer = NULL;
+ unsigned char *apdu;
+ size_t apdulen;
+ int sw;
+ long rc; /* we need a long here due to PC/SC. */
+ int class;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (apdudatalen > 65535)
+ return SW_HOST_INV_VALUE;
+
+ if (apdudatalen > sizeof short_apdu_buffer - 5)
+ {
+ apdu_buffer = xtrymalloc (apdudatalen + 5);
+ if (!apdu_buffer)
+ return SW_HOST_OUT_OF_CORE;
+ apdu = apdu_buffer;
+ }
+ else
+ {
+ apdu = short_apdu_buffer;
+ }
+ apdulen = apdudatalen;
+ memcpy (apdu, apdudata, apdudatalen);
+ class = apdulen? *apdu : 0;
+
+ if (extended_length >= 256 && extended_length <= 65536)
+ {
+ result_buffer_size = extended_length;
+ result_buffer = xtrymalloc (result_buffer_size + 10);
+ if (!result_buffer)
+ {
+ xfree (apdu_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ result = result_buffer;
+ }
+ else
+ {
+ result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+ result = short_result_buffer;
+ }
+#undef SHORT_RESULT_BUFFER_SIZE
+
+ if ((sw = lock_slot (slot)))
+ {
+ xfree (apdu_buffer);
+ xfree (result_buffer);
+ return sw;
+ }
+
+ resultlen = result_buffer_size;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ xfree (apdu_buffer);
+ apdu_buffer = NULL;
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_direct(%d) failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ /* Store away the returned data but strip the statusword. */
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" response: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+ log_printhex (result, resultlen, " dump: ");
+ }
+
+ if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
+ {
+ unsigned char *p = NULL, *tmp;
+ size_t bufsize = 4096;
+
+ /* It is likely that we need to return much more data, so we
+ start off with a large buffer. */
+ if (retbuf)
+ {
+ *retbuf = p = xtrymalloc (bufsize + 2);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ log_assert (resultlen < bufsize);
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+
+ do
+ {
+ int len = (sw & 0x00ff);
+
+ if (DBG_CARD_IO)
+ log_debug ("apdu_send_direct(%d): %d more bytes available\n",
+ slot, len);
+ apdu = short_apdu_buffer;
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = 0xC0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = len;
+ memset (apdu+apdulen, 0, sizeof (short_apdu_buffer) - apdulen);
+ resultlen = result_buffer_size;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_direct(%d) for get response failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" more: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+ log_printhex (result, resultlen, " dump: ");
+ }
+
+ if ((sw & 0xff00) == SW_MORE_DATA
+ || sw == SW_SUCCESS
+ || sw == SW_EOF_REACHED )
+ {
+ if (retbuf && resultlen)
+ {
+ if (p - *retbuf + resultlen > bufsize)
+ {
+ bufsize += resultlen > 4096? resultlen: 4096;
+ tmp = xtryrealloc (*retbuf, bufsize + 2);
+ if (!tmp)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ p = tmp + (p - *retbuf);
+ *retbuf = tmp;
+ }
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+ }
+ else
+ log_info ("apdu_send_direct(%d) "
+ "got unexpected status %04X from get response\n",
+ slot, sw);
+ }
+ while ((sw & 0xff00) == SW_MORE_DATA);
+
+ if (retbuf)
+ {
+ *retbuflen = p - *retbuf;
+ tmp = xtryrealloc (*retbuf, *retbuflen + 2);
+ if (tmp)
+ *retbuf = tmp;
+ }
+ }
+ else
+ {
+ if (retbuf)
+ {
+ *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ xfree (result_buffer);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ *retbuflen = resultlen;
+ memcpy (*retbuf, result, resultlen);
+ }
+ }
+
+ unlock_slot (slot);
+ xfree (result_buffer);
+
+ /* Append the status word. Note that we reserved the two extra
+ bytes while allocating the buffer. */
+ if (retbuf)
+ {
+ (*retbuf)[(*retbuflen)++] = (sw >> 8);
+ (*retbuf)[(*retbuflen)++] = sw;
+ }
+
+ if (r_sw)
+ *r_sw = sw;
+
+ if (DBG_CARD_IO && retbuf)
+ log_printhex (*retbuf, *retbuflen, " dump: ");
+
+
+ return 0;
+}
+
+
+const char *
+apdu_get_reader_name (int slot)
+{
+ return reader_table[slot].rdrname;
+}
+
+
+/* Return the list of currently known readers. Caller must free the
+ * returned value. Might return NULL. */
+char *
+apdu_get_reader_list (void)
+{
+ membuf_t mb;
+ char *ccidlist = NULL;
+
+ init_membuf (&mb, 256);
+#ifdef HAVE_LIBUSB
+ ccidlist = ccid_get_reader_list ();
+#endif
+
+ if (ccidlist && *ccidlist)
+ put_membuf_str (&mb, ccidlist);
+ if (pcsc.reader_list && *pcsc.reader_list)
+ {
+ if (ccidlist && *ccidlist)
+ put_membuf (&mb, "\n", 1);
+ put_membuf_str (&mb, pcsc.reader_list);
+ }
+ xfree (ccidlist);
+ put_membuf (&mb, "", 1);
+
+ return get_membuf (&mb, NULL);
+}
+
+
+gpg_error_t
+apdu_init (void)
+{
+#ifdef USE_NPTH
+ gpg_error_t err;
+ int i;
+
+ pcsc.context = -1;
+ pcsc.context_valid = 0;
+ pcsc.reader_list = NULL;
+
+ if (npth_mutex_init (&reader_table_lock, NULL))
+ goto leave;
+
+ for (i = 0; i < MAX_READER; i++)
+ if (npth_mutex_init (&reader_table[i].lock, NULL))
+ goto leave;
+
+ /* All done well. */
+ return 0;
+
+ leave:
+ err = gpg_error_from_syserror ();
+ log_error ("apdu: error initializing mutex: %s\n", gpg_strerror (err));
+ return err;
+#endif /*USE_NPTH*/
+ return 0;
+}
diff --git a/scd/apdu.h b/scd/apdu.h
new file mode 100644
index 0000000..32b8e9e
--- /dev/null
+++ b/scd/apdu.h
@@ -0,0 +1,156 @@
+/* apdu.h - ISO 7816 APDU functions and low level I/O
+ * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * $Id$
+ */
+
+#ifndef APDU_H
+#define APDU_H
+
+/* ISO 7816 values for the statusword are defined here because they
+ should not be visible to the users of the actual ISO command
+ API. */
+enum {
+ SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
+ masked of.*/
+ SW_EOF_REACHED = 0x6282,
+ SW_TERM_STATE = 0x6285, /* Selected file is in termination state. */
+ SW_EEPROM_FAILURE = 0x6581,
+ SW_WRONG_LENGTH = 0x6700,
+ SW_SM_NOT_SUP = 0x6882, /* Secure Messaging is not supported. */
+ SW_CC_NOT_SUP = 0x6884, /* Command Chaining is not supported. */
+ SW_FILE_STRUCT = 0x6981, /* Command can't be used for file structure. */
+ SW_CHV_WRONG = 0x6982,
+ SW_CHV_BLOCKED = 0x6983,
+ SW_REF_DATA_INV = 0x6984, /* Referenced data invalidated. */
+ SW_USE_CONDITIONS = 0x6985,
+ SW_NO_CURRENT_EF = 0x6986, /* No current EF selected. */
+ SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
+ SW_NOT_SUPPORTED = 0x6a81,
+ SW_FILE_NOT_FOUND = 0x6a82,
+ SW_RECORD_NOT_FOUND = 0x6a83,
+ SW_NOT_ENOUGH_MEMORY= 0x6a84, /* Not enough memory space in the file. */
+ SW_INCONSISTENT_LC = 0x6a85, /* Lc inconsistent with TLV structure. */
+ SW_INCORRECT_P0_P1 = 0x6a86,
+ SW_BAD_LC = 0x6a87, /* Lc does not match command or p1/p2. */
+ SW_REF_NOT_FOUND = 0x6a88,
+ SW_BAD_P0_P1 = 0x6b00,
+ SW_EXACT_LENGTH = 0x6c00,
+ SW_INS_NOT_SUP = 0x6d00,
+ SW_CLA_NOT_SUP = 0x6e00,
+ SW_SUCCESS = 0x9000,
+
+ /* The following statuswords are no real ones but used to map host
+ OS errors into status words. A status word is 16 bit so that
+ those values can't be issued by a card. */
+ SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
+ between errnos on a failed malloc. */
+ SW_HOST_INV_VALUE = 0x10002,
+ SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
+ SW_HOST_NO_DRIVER = 0x10004,
+ SW_HOST_NOT_SUPPORTED = 0x10005,
+ SW_HOST_LOCKING_FAILED= 0x10006,
+ SW_HOST_BUSY = 0x10007,
+ SW_HOST_NO_CARD = 0x10008,
+ SW_HOST_CARD_INACTIVE = 0x10009,
+ SW_HOST_CARD_IO_ERROR = 0x1000a,
+ SW_HOST_GENERAL_ERROR = 0x1000b,
+ SW_HOST_NO_READER = 0x1000c,
+ SW_HOST_ABORTED = 0x1000d,
+ SW_HOST_NO_PINPAD = 0x1000e,
+ SW_HOST_ALREADY_CONNECTED = 0x1000f,
+ SW_HOST_CANCELLED = 0x10010,
+ SW_HOST_DEVICE_ACCESS = 0x10011,
+ SW_HOST_USB_OTHER = 0x10020,
+ SW_HOST_USB_IO = 0x10021,
+ SW_HOST_USB_ACCESS = 0x10023,
+ SW_HOST_USB_NO_DEVICE = 0x10024,
+ SW_HOST_USB_BUSY = 0x10026,
+ SW_HOST_USB_TIMEOUT = 0x10027,
+ SW_HOST_USB_OVERFLOW = 0x10028
+};
+
+struct dev_list;
+
+#define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
+
+
+/* Bit flags for the card status. */
+#define APDU_CARD_USABLE (1) /* Card is present and ready for use. */
+#define APDU_CARD_PRESENT (2) /* Card is just present. */
+#define APDU_CARD_ACTIVE (4) /* Card is active. */
+
+
+gpg_error_t apdu_init (void);
+
+gpg_error_t apdu_dev_list_start (const char *portstr, struct dev_list **l_p);
+void apdu_dev_list_finish (struct dev_list *l);
+
+/* Note, that apdu_open_reader returns no status word but -1 on error. */
+int apdu_open_reader (struct dev_list *l, int app_empty);
+int apdu_open_remote_reader (const char *portstr,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value);
+int apdu_close_reader (int slot);
+void apdu_prepare_exit (void);
+int apdu_enum_reader (int slot, int *used);
+unsigned char *apdu_get_atr (int slot, size_t *atrlen);
+
+const char *apdu_strerror (int rc);
+
+
+/* These APDU functions return status words. */
+
+int apdu_connect (int slot);
+int apdu_disconnect (int slot);
+
+int apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg);
+int apdu_set_prompt_cb (int slot, void (*cb) (void *, int), void *cb_arg);
+
+int apdu_reset (int slot);
+int apdu_get_status (int slot, int hang, unsigned int *status);
+int apdu_check_pinpad (int slot, int command, pininfo_t *pininfo);
+int apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+int apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+int apdu_send_simple (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const char *data);
+int apdu_send (int slot, int extended_mode,
+ int class, int ins, int p0, int p1, int lc, const char *data,
+ unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_le (int slot, int extended_mode,
+ int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_direct (int slot, size_t extended_length,
+ const unsigned char *apdudata, size_t apdudatalen,
+ int handle_more, unsigned int *r_sw,
+ unsigned char **retbuf, size_t *retbuflen);
+const char *apdu_get_reader_name (int slot);
+char *apdu_get_reader_list (void);
+
+#endif /*APDU_H*/
diff --git a/scd/app-common.h b/scd/app-common.h
new file mode 100644
index 0000000..f95db74
--- /dev/null
+++ b/scd/app-common.h
@@ -0,0 +1,306 @@
+/* app-common.h - Common declarations for all card applications
+ * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * $Id$
+ */
+
+#ifndef GNUPG_SCD_APP_COMMON_H
+#define GNUPG_SCD_APP_COMMON_H
+
+#include <npth.h>
+#include <ksba.h>
+
+/* Flags used with app_change_pin. */
+#define APP_CHANGE_FLAG_RESET 1 /* PIN Reset mode. */
+#define APP_CHANGE_FLAG_NULLPIN 2 /* NULL PIN mode. */
+#define APP_CHANGE_FLAG_CLEAR 4 /* Clear the given PIN. */
+
+/* Flags used with app_genkey. */
+#define APP_GENKEY_FLAG_FORCE 1 /* Force overwriting existing key. */
+
+/* Flags used with app_writekey. */
+#define APP_WRITEKEY_FLAG_FORCE 1 /* Force overwriting existing key. */
+
+/* Flags used with app_readkey. */
+#define APP_READKEY_FLAG_INFO 1 /* Send also a KEYPAIRINFO line. */
+#define APP_READKEY_FLAG_ADVANCED 2 /* (gnupg 2.2 only) */
+
+/* Bit flags set by the decipher function into R_INFO. */
+#define APP_DECIPHER_INFO_NOPAD 1 /* Padding has been removed. */
+
+/* Flags used by the app_write_learn_status. */
+#define APP_LEARN_FLAG_KEYPAIRINFO 1 /* Return only keypair infos. */
+#define APP_LEARN_FLAG_MULTI 2 /* Return info for all apps. */
+#define APP_LEARN_FLAG_REREAD 4 /* Re-read infos from the token. */
+
+
+/* List of supported card types. Generic is the usual ISO7817-4
+ * compliant card. More specific card or token versions can be given
+ * here. Introduced in 2.2 for easier backporting from 2.3. */
+typedef enum
+ {
+ CARDTYPE_GENERIC = 0,
+ CARDTYPE_GNUK,
+ CARDTYPE_YUBIKEY,
+ CARDTYPE_ZEITCONTROL
+ } cardtype_t;
+
+
+/* List of supported card applications. The source code for each
+ * application can usually be found in an app-NAME.c file. Introduced
+ * in 2.2 for easier backporting from 2.3. */
+typedef enum
+ {
+ APPTYPE_NONE = 0,
+ APPTYPE_UNDEFINED,
+ APPTYPE_OPENPGP,
+ APPTYPE_PIV,
+ APPTYPE_NKS,
+ APPTYPE_P15,
+ APPTYPE_GELDKARTE,
+ APPTYPE_DINSIG,
+ APPTYPE_SC_HSM
+ } apptype_t;
+
+
+/* Forward declarations. */
+struct app_ctx_s;
+struct app_local_s; /* Defined by all app-*.c. */
+
+typedef struct app_ctx_s *app_t;
+
+struct app_ctx_s {
+ struct app_ctx_s *next;
+
+ npth_mutex_t lock;
+
+ /* Number of connections currently using this application context.
+ If this is not 0 the application has been initialized and the
+ function pointers may be used. Note that for unsupported
+ operations the particular function pointer is set to NULL */
+ unsigned int ref_count;
+
+ /* Used reader slot. */
+ int slot;
+
+ unsigned char *serialno; /* Serialnumber in raw form, allocated. */
+ size_t serialnolen; /* Length in octets of serialnumber. */
+ apptype_t apptype;
+ unsigned int appversion; /* Version of the application or 0. */
+ cardtype_t cardtype; /* The token's type. */
+ unsigned int cardversion;/* Firmware version of the token or 0. */
+ unsigned int card_status;
+ unsigned int reset_requested:1;
+ unsigned int periodical_check_needed:1;
+ unsigned int did_chv1:1;
+ unsigned int force_chv1:1; /* True if the card does not cache CHV1. */
+ unsigned int did_chv2:1;
+ unsigned int did_chv3:1;
+ struct app_local_s *app_local; /* Local to the application. */
+ struct {
+ void (*deinit) (app_t app);
+
+ /* prep_reselect and reselect are not used in this version of scd. */
+ gpg_error_t (*prep_reselect) (app_t app, ctrl_t ctrl);
+ gpg_error_t (*reselect) (app_t app, ctrl_t ctrl);
+
+ gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
+ gpg_error_t (*readcert) (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen);
+ gpg_error_t (*readkey) (app_t app, ctrl_t ctrl,
+ const char *certid, unsigned int flags,
+ unsigned char **pk, size_t *pklen);
+ gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name);
+ gpg_error_t (*setattr) (app_t app, ctrl_t ctrl, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+ gpg_error_t (*sign) (app_t app, ctrl_t ctrl,
+ const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen );
+ gpg_error_t (*auth) (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+ gpg_error_t (*decipher) (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info);
+ gpg_error_t (*writecert) (app_t app, ctrl_t ctrl,
+ const char *certid,
+ gpg_error_t (*pincb)(void*,const char *,char **),
+ void *pincb_arg,
+ const unsigned char *data, size_t datalen);
+ gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*,const char *,char **),
+ void *pincb_arg,
+ const unsigned char *pk, size_t pklen);
+ gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
+ const char *keyref, const char *keytype,
+ unsigned int flags, time_t createtime,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl,
+ const char *chvnostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ gpg_error_t (*check_pin) (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+
+ /* with_keygrip is not used in this version of scd but having it
+ * makes back porting app-*.c from later versions easier. */
+ gpg_error_t (*with_keygrip) (app_t app, ctrl_t ctrl, int action,
+ const char *keygrip_str, int capability);
+ } fnc;
+};
+
+
+/* Action values for app_do_with_keygrip. */
+enum
+ {
+ KEYGRIP_ACTION_SEND_DATA,
+ KEYGRIP_ACTION_WRITE_STATUS,
+ KEYGRIP_ACTION_LOOKUP
+ };
+
+
+/* Helper to get the slot from an APP object. */
+static inline int
+app_get_slot (app_t app)
+{
+ /* Note that this is a similar function of the one in 2.3 which we
+ * use to make back porting easier. */
+ if (app)
+ return app->slot;
+ return -1;
+}
+
+/* Macro to access members in app_t which are found in 2.3 in a linked
+ * card_t member. */
+#define APP_CARD(a) (a)
+
+
+/*-- app-help.c --*/
+unsigned int app_help_count_bits (const unsigned char *a, size_t len);
+gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
+ char *hexkeygrip,
+ gcry_sexp_t *r_pkey,
+ int *r_algo, char **r_algostr);
+gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip,
+ gcry_sexp_t *r_pkey, int *r_algo);
+gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen,
+ unsigned char **r_pk, size_t *r_pklen);
+size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
+
+
+/*-- app.c --*/
+void app_send_card_list (ctrl_t ctrl);
+char *app_get_serialno (app_t app);
+char *app_get_dispserialno (app_t app, int nofallback);
+
+void app_dump_state (void);
+void application_notify_card_reset (int slot);
+gpg_error_t check_application_conflict (const char *name, app_t app);
+gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset);
+gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app,
+ int scan, const unsigned char *serialno_bin,
+ size_t serialno_bin_len);
+char *get_supported_applications (void);
+void release_application (app_t app, int locked_already);
+gpg_error_t app_munge_serialno (app_t app);
+gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl,
+ unsigned int flags);
+gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid,
+ unsigned char **cert, size_t *certlen);
+gpg_error_t app_readkey (app_t app, ctrl_t ctrl, int advanced,
+ const char *keyid, unsigned char **pk, size_t *pklen);
+gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name);
+gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+gpg_error_t app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+gpg_error_t app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info);
+gpg_error_t app_writecert (app_t app, ctrl_t ctrl,
+ const char *certidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen);
+gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
+ const char *keyidstr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen);
+gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
+ const char *keynostr, const char *keytype,
+ unsigned int flags, time_t createtime,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+gpg_error_t app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes,
+ unsigned char *buffer);
+gpg_error_t app_change_pin (app_t app, ctrl_t ctrl,
+ const char *chvnostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+gpg_error_t app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+
+
+/*-- app-openpgp.c --*/
+gpg_error_t app_select_openpgp (app_t app);
+
+/*-- app-nks.c --*/
+gpg_error_t app_select_nks (app_t app);
+
+/*-- app-dinsig.c --*/
+gpg_error_t app_select_dinsig (app_t app);
+
+/*-- app-p15.c --*/
+gpg_error_t app_select_p15 (app_t app);
+
+/*-- app-geldkarte.c --*/
+gpg_error_t app_select_geldkarte (app_t app);
+
+/*-- app-sc-hsm.c --*/
+gpg_error_t app_select_sc_hsm (app_t app);
+
+
+#endif /*GNUPG_SCD_APP_COMMON_H*/
diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c
new file mode 100644
index 0000000..5a2713e
--- /dev/null
+++ b/scd/app-dinsig.c
@@ -0,0 +1,574 @@
+/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
+ * Copyright (C) 2002, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+/* The German signature law and its bylaw (SigG and SigV) is currently
+ used with an interface specification described in DIN V 66291-1.
+ The AID to be used is: 'D27600006601'.
+
+ The file IDs for certificates utilize the generic format:
+ Cxyz
+ C being the hex digit 'C' (12).
+ x being the service indicator:
+ '0' := SigG conform digital signature.
+ '1' := entity authentication.
+ '2' := key encipherment.
+ '3' := data encipherment.
+ '4' := key agreement.
+ other values are reserved for future use.
+ y being the security environment number using '0' for cards
+ not supporting a SE number.
+ z being the certificate type:
+ '0' := C.CH (base certificate of card holder) or C.ICC.
+ '1' .. '7' := C.CH (business or professional certificate
+ of card holder.
+ '8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA).
+ 'E' := C.RCA (self certified certificate of the Root-CA).
+ 'F' := reserved.
+
+ The file IDs used by default are:
+ '1F00' EF.SSD (security service descriptor). [o,o]
+ '2F02' EF.GDO (global data objects) [m,m]
+ 'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte.
+ Read and update after user authentication. [o,o]
+ 'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size
+ of keys. [m (unless a 'C00E' is present),m]
+ 'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size
+ of keys. [o,o]
+ 'C00n' EF.C.CH.DS (digital signature certificate of card holder)
+ with n := 0 .. 7. Size is 2k or size of cert. Read and
+ update allowed after user authentication. [m,m]
+ 'C00m' EF.C.CA.DS (digital signature certificate of CA)
+ with m := 8 .. E. Size is 1k or size of cert. Read always
+ allowed, update after user authentication. [o,o]
+ 'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m]
+ 'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m]
+ 'D000' EF.DM (display message) [-,m]
+
+ The letters in brackets indicate optional or mandatory files: The
+ first for card terminals under full control and the second for
+ "business" card terminals.
+*/
+
+
+
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "scdaemon.h"
+
+#include "../common/i18n.h"
+#include "iso7816.h"
+#include "../common/tlv.h"
+
+
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err;
+ char ct_buf[100], id_buf[100];
+ char hexkeygrip[41];
+ size_t len, certoff;
+ unsigned char *der;
+ size_t derlen;
+ ksba_cert_t cert;
+ int fid;
+
+ (void)flags;
+
+ /* Return the certificate of the card holder. */
+ fid = 0xC000;
+ len = app_help_read_length_of_cert (app->slot, fid, &certoff);
+ if (!len)
+ return 0; /* Card has not been personalized. */
+
+ sprintf (ct_buf, "%d", 101);
+ sprintf (id_buf, "DINSIG.%04X", fid);
+ send_status_info (ctrl, "CERTINFO",
+ ct_buf, strlen (ct_buf),
+ id_buf, strlen (id_buf),
+ NULL, (size_t)0);
+
+ /* Now we need to read the certificate, so that we can get the
+ public key out of it. */
+ err = iso7816_read_binary (app->slot, certoff, len-certoff, &der, &derlen);
+ if (err)
+ {
+ log_info ("error reading entire certificate from FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return 0;
+ }
+
+ err = ksba_cert_new (&cert);
+ if (err)
+ {
+ xfree (der);
+ return err;
+ }
+ err = ksba_cert_init_from_mem (cert, der, derlen);
+ xfree (der); der = NULL;
+ if (err)
+ {
+ log_error ("failed to parse the certificate at FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ ksba_cert_release (cert);
+ return err;
+ }
+ err = app_help_get_keygrip_string (cert, hexkeygrip, NULL, NULL);
+ if (err)
+ {
+ log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ ksba_cert_release (cert);
+
+ sprintf (id_buf, "DINSIG.%04X", fid);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ hexkeygrip, 40,
+ id_buf, strlen (id_buf),
+ NULL, (size_t)0);
+ return 0;
+}
+
+
+
+
+/* Read the certificate with id CERTID (as returned by learn_status in
+ the CERTINFO status lines) and return it in the freshly allocated
+ buffer put into CERT and the length of the certificate put into
+ CERTLEN.
+
+ FIXME: This needs some cleanups and caching with do_learn_status.
+*/
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen)
+{
+ int fid;
+ gpg_error_t err;
+ unsigned char *buffer;
+ const unsigned char *p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t totobjlen, objlen, hdrlen;
+ int rootca = 0;
+
+ *cert = NULL;
+ *certlen = 0;
+ if (strncmp (certid, "DINSIG.", 7) )
+ return gpg_error (GPG_ERR_INV_ID);
+ certid += 7;
+ if (!hexdigitp (certid) || !hexdigitp (certid+1)
+ || !hexdigitp (certid+2) || !hexdigitp (certid+3)
+ || certid[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (certid);
+ if (fid != 0xC000 )
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ /* Read the entire file. fixme: This could be optimized by first
+ reading the header to figure out how long the certificate
+ actually is. */
+ err = iso7816_select_file (app->slot, fid, 0);
+ if (err)
+ {
+ log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
+ if (err)
+ {
+ log_error ("error reading certificate from FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return err;
+ }
+
+ if (!buflen || *buffer == 0xff)
+ {
+ log_info ("no certificate contained in FID 0x%04X\n", fid);
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+
+ /* Now figure something out about the object. */
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
+ ;
+ else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
+ rootca = 1;
+ else
+ return gpg_error (GPG_ERR_INV_OBJ);
+ totobjlen = objlen + hdrlen;
+ assert (totobjlen <= buflen);
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (rootca)
+ ;
+ else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
+ {
+ const unsigned char *save_p;
+
+ /* The certificate seems to be contained in a userCertificate
+ container. Skip this and assume the following sequence is
+ the certificate. */
+ if (n < objlen)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ p += objlen;
+ n -= objlen;
+ save_p = p;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
+ return gpg_error (GPG_ERR_INV_OBJ);
+ totobjlen = objlen + hdrlen;
+ assert (save_p + totobjlen <= buffer + buflen);
+ memmove (buffer, save_p, totobjlen);
+ }
+
+ *cert = buffer;
+ buffer = NULL;
+ *certlen = totobjlen;
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Verify the PIN if required. */
+static gpg_error_t
+verify_pin (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ const char *s;
+ int rc;
+ pininfo_t pininfo;
+
+ if ( app->did_chv1 && !app->force_chv1 )
+ return 0; /* No need to verify it again. */
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = 6;
+ pininfo.maxlen = 8;
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
+ {
+ rc = pincb (pincb_arg,
+ _("||Please enter your PIN at the reader's pinpad"),
+ NULL);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+ rc = iso7816_verify_kp (app->slot, 0x81, &pininfo);
+ /* Dismiss the prompt. */
+ pincb (pincb_arg, NULL, NULL);
+ }
+ else /* No Pinpad. */
+ {
+ char *pinvalue;
+
+ rc = pincb (pincb_arg, "PIN", &pinvalue);
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ /* We require the PIN to be at least 6 and at max 8 bytes.
+ According to the specs, this should all be ASCII. */
+ for (s=pinvalue; digitp (s); s++)
+ ;
+ if (*s)
+ {
+ log_error ("Non-numeric digits found in PIN\n");
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ if (strlen (pinvalue) < pininfo.minlen)
+ {
+ log_error ("PIN is too short; minimum length is %d\n",
+ pininfo.minlen);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else if (strlen (pinvalue) > pininfo.maxlen)
+ {
+ log_error ("PIN is too large; maximum length is %d\n",
+ pininfo.maxlen);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+ if (gpg_err_code (rc) == GPG_ERR_INV_VALUE)
+ {
+ /* We assume that ISO 9564-1 encoding is used and we failed
+ because the first nibble we passed was 3 and not 2. DIN
+ says something about looking up such an encoding in the
+ SSD but I was not able to find any tag relevant to
+ this. */
+ char paddedpin[8];
+ int i, ndigits;
+
+ for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
+ ;
+ i = 0;
+ paddedpin[i++] = 0x20 | (ndigits & 0x0f);
+ for (s=pinvalue; i < sizeof paddedpin && *s && s[1]; s = s+2 )
+ paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f));
+ if (i < sizeof paddedpin && *s)
+ paddedpin[i++] = (((*s - '0') << 4) | 0x0f);
+ while (i < sizeof paddedpin)
+ paddedpin[i++] = 0xff;
+ rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin);
+ }
+ xfree (pinvalue);
+ }
+
+ if (rc)
+ {
+ log_error ("verify PIN failed\n");
+ return rc;
+ }
+ app->did_chv1 = 1;
+ return 0;
+}
+
+
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN;
+ that callback should return the PIN in an allocated buffer and
+ store that in the 3rd argument. */
+static gpg_error_t
+do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char sha256_prefix[19] = /* OID is 2.16.840.1.101.3.4.2.1 */
+ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20 };
+ int rc;
+ int fid;
+ unsigned char data[19+32]; /* Must be large enough for a SHA-256 digest
+ + the largest OID _prefix above. */
+ int datalen;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (indatalen != 20 && indatalen != 16 && indatalen != 32
+ && indatalen != (15+20) && indatalen != (19+32))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check that the provided ID is vaid. This is not really needed
+ but we do it to enforce correct usage by the caller. */
+ if (strncmp (keyidstr, "DINSIG.", 7) )
+ return gpg_error (GPG_ERR_INV_ID);
+ keyidstr += 7;
+ if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
+ || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
+ || keyidstr[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (keyidstr);
+ if (fid != 0xC000)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ /* Prepare the DER object from INDATA. */
+ datalen = 35;
+ if (indatalen == 15+20)
+ {
+ /* Alright, the caller was so kind to send us an already
+ prepared DER object. Check that it is what we want and that
+ it matches the hash algorithm. */
+ if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
+ ;
+ else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
+ ;
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data, indata, indatalen);
+ }
+ else if (indatalen == 19+32)
+ {
+ /* Alright, the caller was so kind to send us an already
+ prepared DER object. Check that it is what we want and that
+ it matches the hash algorithm. */
+ datalen = indatalen;
+ if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
+ ;
+ else if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha256_prefix, 19))
+ {
+ /* Fixme: This is a kludge. A better solution is not to use
+ SHA1 as default but use an autodetection. However this
+ needs changes in all app-*.c */
+ datalen = indatalen;
+ }
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data, indata, indatalen);
+ }
+ else
+ {
+ int len = 15;
+ if (hashalgo == GCRY_MD_SHA1)
+ memcpy (data, sha1_prefix, len);
+ else if (hashalgo == GCRY_MD_RMD160)
+ memcpy (data, rmd160_prefix, len);
+ else if (hashalgo == GCRY_MD_SHA256)
+ {
+ len = 19;
+ datalen = len + indatalen;
+ memcpy (data, sha256_prefix, len);
+ }
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data+len, indata, indatalen);
+ }
+
+ rc = verify_pin (app, pincb, pincb_arg);
+ if (!rc)
+ rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0,
+ outdata, outdatalen);
+ return rc;
+}
+
+
+#if 0
+#warning test function - works but may brick your card
+/* Handle the PASSWD command. CHVNOSTR is currently ignored; we
+ always use VHV0. RESET_MODE is not yet implemented. */
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+ unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ char *pinvalue;
+ const char *oldpin;
+ size_t oldpinlen;
+
+ if ((flags & APP_CHANGE_FLAG_RESET))
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ if ((flags & APP_CHANGE_FLAG_NULLPIN))
+ {
+ /* With the nullpin flag, we do not verify the PIN - it would fail
+ if the Nullpin is still set. */
+ oldpin = "\0\0\0\0\0";
+ oldpinlen = 6;
+ }
+ else
+ {
+ err = verify_pin (app, pincb, pincb_arg);
+ if (err)
+ return err;
+ oldpin = NULL;
+ oldpinlen = 0;
+ }
+
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ err = pincb (pincb_arg, _("|N|Initial New PIN"), &pinvalue);
+ if (err)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_change_reference_data (app->slot, 0x81,
+ oldpin, oldpinlen,
+ pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ return err;
+}
+#endif /*0*/
+
+
+/* Select the DINSIG application on the card in SLOT. This function
+ must be used before any other DINSIG application functions. */
+gpg_error_t
+app_select_dinsig (app_t app)
+{
+ static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
+ int slot = app->slot;
+ int rc;
+
+ rc = iso7816_select_application (slot, aid, sizeof aid, 0);
+ if (!rc)
+ {
+ app->apptype = APPTYPE_DINSIG;
+
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = do_readcert;
+ app->fnc.getattr = NULL;
+ app->fnc.setattr = NULL;
+ app->fnc.genkey = NULL;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = NULL;
+ app->fnc.decipher = NULL;
+ app->fnc.change_pin = NULL /*do_change_pin*/;
+ app->fnc.check_pin = NULL;
+
+ app->force_chv1 = 1;
+ }
+
+ return rc;
+}
diff --git a/scd/app-geldkarte.c b/scd/app-geldkarte.c
new file mode 100644
index 0000000..5437263
--- /dev/null
+++ b/scd/app-geldkarte.c
@@ -0,0 +1,408 @@
+/* app-geldkarte.c - The German Geldkarte application
+ * Copyright (C) 2004 g10 Code GmbH
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+/* This is a read-only application to quickly dump information of a
+ German Geldkarte (debit card for small amounts). We only support
+ newer Geldkarte (with the AID DF_BOERSE_NEU) issued since 2000 or
+ even earlier.
+*/
+
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "scdaemon.h"
+
+#include "../common/i18n.h"
+#include "iso7816.h"
+#include "../common/tlv.h"
+
+
+
+/* Object with application (i.e. Geldkarte) specific data. */
+struct app_local_s
+{
+ char kblz[2+1+4+1];
+ const char *banktype;
+ char *cardno;
+ char expires[7+1];
+ char validfrom[10+1];
+ char *country;
+ char currency[3+1];
+ unsigned int currency_mult100;
+ unsigned char chipid;
+ unsigned char osvers;
+ int balance;
+ int maxamount;
+ int maxamount1;
+};
+
+
+
+
+/* Deconstructor. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ xfree (app->app_local->cardno);
+ xfree (app->app_local->country);
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+static gpg_error_t
+send_one_string (ctrl_t ctrl, const char *name, const char *string)
+{
+ if (!name || !string)
+ return 0;
+ send_status_info (ctrl, name, string, strlen (string), NULL, 0);
+ return 0;
+}
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err;
+ struct app_local_s *ld = app->app_local;
+ char numbuf[100];
+
+ if (!strcmp (name, "X-KBLZ"))
+ err = send_one_string (ctrl, name, ld->kblz);
+ else if (!strcmp (name, "X-BANKINFO"))
+ err = send_one_string (ctrl, name, ld->banktype);
+ else if (!strcmp (name, "X-CARDNO"))
+ err = send_one_string (ctrl, name, ld->cardno);
+ else if (!strcmp (name, "X-EXPIRES"))
+ err = send_one_string (ctrl, name, ld->expires);
+ else if (!strcmp (name, "X-VALIDFROM"))
+ err = send_one_string (ctrl, name, ld->validfrom);
+ else if (!strcmp (name, "X-COUNTRY"))
+ err = send_one_string (ctrl, name, ld->country);
+ else if (!strcmp (name, "X-CURRENCY"))
+ err = send_one_string (ctrl, name, ld->currency);
+ else if (!strcmp (name, "X-ZKACHIPID"))
+ {
+ snprintf (numbuf, sizeof numbuf, "0x%02X", ld->chipid);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-OSVERSION"))
+ {
+ snprintf (numbuf, sizeof numbuf, "0x%02X", ld->osvers);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-BALANCE"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%.2f",
+ (double)ld->balance / 100 * ld->currency_mult100);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-MAXAMOUNT"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%.2f",
+ (double)ld->maxamount / 100 * ld->currency_mult100);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else if (!strcmp (name, "X-MAXAMOUNT1"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%.2f",
+ (double)ld->maxamount1 / 100 * ld->currency_mult100);
+ err = send_one_string (ctrl, name, numbuf);
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_NAME);
+
+ return err;
+}
+
+
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ static const char *names[] = {
+ "X-KBLZ",
+ "X-BANKINFO",
+ "X-CARDNO",
+ "X-EXPIRES",
+ "X-VALIDFROM",
+ "X-COUNTRY",
+ "X-CURRENCY",
+ "X-ZKACHIPID",
+ "X-OSVERSION",
+ "X-BALANCE",
+ "X-MAXAMOUNT",
+ "X-MAXAMOUNT1",
+ NULL
+ };
+ gpg_error_t err = 0;
+ int idx;
+
+ (void)flags;
+
+ for (idx=0; names[idx] && !err; idx++)
+ err = do_getattr (app, ctrl, names[idx]);
+ return err;
+}
+
+
+static char *
+copy_bcd (const unsigned char *string, size_t length)
+{
+ const unsigned char *s;
+ size_t n;
+ size_t needed;
+ char *buffer, *dst;
+
+ if (!length)
+ return xtrystrdup ("");
+
+ /* Skip leading zeroes. */
+ for (; length && !*string; length--, string++)
+ ;
+ s = string;
+ n = length;
+ needed = 0;
+ for (; n ; n--, s++)
+ {
+ if (!needed && !(*s & 0xf0))
+ ; /* Skip the leading zero in the first nibble. */
+ else
+ {
+ if ( ((*s >> 4) & 0x0f) > 9 )
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ needed++;
+ }
+ if ( n == 1 && (*s & 0x0f) > 9 )
+ ; /* Ignore the last digit if it has the sign. */
+ else
+ {
+ needed++;
+ if ( (*s & 0x0f) > 9 )
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ }
+
+ }
+ if (!needed) /* If it is all zero, print a "0". */
+ needed++;
+
+ buffer = dst = xtrymalloc (needed+1);
+ if (!buffer)
+ return NULL;
+
+ s = string;
+ n = length;
+ needed = 0;
+ for (; n ; n--, s++)
+ {
+ if (!needed && !(*s & 0xf0))
+ ; /* Skip the leading zero in the first nibble. */
+ else
+ {
+ *dst++ = '0' + ((*s >> 4) & 0x0f);
+ needed++;
+ }
+
+ if ( n == 1 && (*s & 0x0f) > 9 )
+ ; /* Ignore the last digit if it has the sign. */
+ else
+ {
+ *dst++ = '0' + (*s & 0x0f);
+ needed++;
+ }
+ }
+ if (!needed)
+ *dst++ = '0';
+ *dst = 0;
+
+ return buffer;
+}
+
+
+/* Convert the BCD number at STING of LENGTH into an integer and store
+ that at RESULT. Return 0 on success. */
+static gpg_error_t
+bcd_to_int (const unsigned char *string, size_t length, int *result)
+{
+ char *tmp;
+
+ tmp = copy_bcd (string, length);
+ if (!tmp)
+ return gpg_error (GPG_ERR_BAD_DATA);
+ *result = strtol (tmp, NULL, 10);
+ xfree (tmp);
+ return 0;
+}
+
+
+/* Select the Geldkarte application. */
+gpg_error_t
+app_select_geldkarte (app_t app)
+{
+ static char const aid[] =
+ { 0xD2, 0x76, 0x00, 0x00, 0x25, 0x45, 0x50, 0x02, 0x00 };
+ gpg_error_t err;
+ int slot = app->slot;
+ unsigned char *result = NULL;
+ size_t resultlen;
+ struct app_local_s *ld;
+ const char *banktype;
+
+ err = iso7816_select_application (slot, aid, sizeof aid, 0);
+ if (err)
+ goto leave;
+
+ /* Read the first record of EF_ID (SFI=0x17). We require this
+ record to be at least 24 bytes with the first byte 0x67 and a
+ correct filler byte. */
+ err = iso7816_read_record (slot, 1, 1, ((0x17 << 3)|4), &result, &resultlen);
+ if (err)
+ goto leave; /* Oops - not a Geldkarte. */
+ if (resultlen < 24 || *result != 0x67 || result[22])
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+
+ /* The short Bankleitzahl consists of 3 bytes at offset 1. */
+ switch (result[1])
+ {
+ case 0x21: banktype = "Oeffentlich-rechtliche oder private Bank"; break;
+ case 0x22: banktype = "Privat- oder Geschaeftsbank"; break;
+ case 0x25: banktype = "Sparkasse"; break;
+ case 0x26:
+ case 0x29: banktype = "Genossenschaftsbank"; break;
+ default:
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave; /* Probably not a Geldkarte. */
+ }
+
+ app->apptype = APPTYPE_GELDKARTE;
+ app->fnc.deinit = do_deinit;
+
+ /* If we don't have a serialno yet construct it from the EF_ID. */
+ if (!app->serialno)
+ {
+ app->serialno = xtrymalloc (10);
+ if (!app->serialno)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (app->serialno, result, 10);
+ app->serialnolen = 10;
+ err = app_munge_serialno (app);
+ if (err)
+ goto leave;
+ }
+
+
+ app->app_local = ld = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->kblz, sizeof ld->kblz, "%02X-%02X%02X",
+ result[1], result[2], result[3]);
+ ld->banktype = banktype;
+ ld->cardno = copy_bcd (result+4, 5);
+ if (!ld->cardno)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->expires, sizeof ld->expires, "20%02X-%02X",
+ result[10], result[11]);
+ snprintf (ld->validfrom, sizeof ld->validfrom, "20%02X-%02X-%02X",
+ result[12], result[13], result[14]);
+
+ ld->country = copy_bcd (result+15, 2);
+ if (!ld->country)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ snprintf (ld->currency, sizeof ld->currency, "%c%c%c",
+ isascii (result[17])? result[17]:' ',
+ isascii (result[18])? result[18]:' ',
+ isascii (result[19])? result[19]:' ');
+
+ ld->currency_mult100 = (result[20] == 0x01? 1:
+ result[20] == 0x02? 10:
+ result[20] == 0x04? 100:
+ result[20] == 0x08? 1000:
+ result[20] == 0x10? 10000:
+ result[20] == 0x20? 100000:0);
+
+ ld->chipid = result[21];
+ ld->osvers = result[23];
+
+ /* Read the first record of EF_BETRAG (SFI=0x18). */
+ xfree (result);
+ err = iso7816_read_record (slot, 1, 1, ((0x18 << 3)|4), &result, &resultlen);
+ if (err)
+ goto leave; /* It does not make sense to continue. */
+ if (resultlen < 12)
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+ err = bcd_to_int (result+0, 3, &ld->balance);
+ if (!err)
+ err = bcd_to_int (result+3, 3, &ld->maxamount);
+ if (!err)
+ err = bcd_to_int (result+6, 3, &ld->maxamount1);
+ /* The next 3 bytes are the maximum amount chargable without using a
+ MAC. This is usually 0. */
+ if (err)
+ goto leave;
+
+ /* Setup the rest of the methods. */
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.getattr = do_getattr;
+
+
+ leave:
+ xfree (result);
+ if (err)
+ do_deinit (app);
+ return err;
+}
diff --git a/scd/app-help.c b/scd/app-help.c
new file mode 100644
index 0000000..8d225ef
--- /dev/null
+++ b/scd/app-help.c
@@ -0,0 +1,285 @@
+/* app-help.c - Application helper functions
+ * Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "scdaemon.h"
+#include "iso7816.h"
+#include "../common/tlv.h"
+
+
+/* Count the number of bits, assuming the A represents an unsigned big
+ integer of length LEN bytes. If A is NULL a length of 0 is
+ returned. */
+unsigned int
+app_help_count_bits (const unsigned char *a, size_t len)
+{
+ unsigned int n = len * 8;
+ int i;
+
+ if (!a)
+ return 0;
+
+ for (; len && !*a; len--, a++, n -=8)
+ ;
+ if (len)
+ {
+ for (i=7; i && !(*a & (1<<i)); i--)
+ n--;
+ }
+ return n;
+}
+
+
+/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
+ * as an hex encoded string in the user provided buffer HEXKEYGRIP
+ * which must be of at least 41 bytes. If R_PKEY is not NULL and the
+ * function succeeded, the S-expression representing the key is stored
+ * there. The caller needs to call gcry_sexp_release on that. If
+ * R_ALGO is not NULL the public key algorithm id of Libgcrypt is
+ * stored there. If R_ALGOSTR is not NULL and the function succeeds a
+ * newly allocated algo string (e.g. "rsa2048") is stored there.
+ * HEXKEYGRIP may be NULL if the caller is not interested in it. */
+gpg_error_t
+app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip,
+ gcry_sexp_t *r_pkey, int *r_algo,
+ char **r_algostr)
+{
+ gpg_error_t err;
+ gcry_sexp_t s_pkey;
+ unsigned char array[KEYGRIP_LEN];
+
+ if (r_pkey)
+ *r_pkey = NULL;
+ if (r_algostr)
+ *r_algostr = NULL;
+
+ err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
+ if (err)
+ return err; /* Can't parse that S-expression. */
+ if (hexkeygrip && !gcry_pk_get_keygrip (s_pkey, array))
+ {
+ gcry_sexp_release (s_pkey);
+ return gpg_error (GPG_ERR_GENERAL); /* Failed to calculate the keygrip.*/
+ }
+
+ if (r_algo)
+ *r_algo = get_pk_algo_from_key (s_pkey);
+
+ if (r_algostr)
+ {
+ *r_algostr = pubkey_algo_string (s_pkey, NULL);
+ if (!*r_algostr)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_sexp_release (s_pkey);
+ return err;
+ }
+ }
+
+ if (r_pkey)
+ *r_pkey = s_pkey;
+ else
+ gcry_sexp_release (s_pkey);
+
+ if (hexkeygrip)
+ bin2hex (array, KEYGRIP_LEN, hexkeygrip);
+
+ return 0;
+}
+
+
+/* Return the KEYGRIP for the certificate CERT as an hex encoded
+ * string in the user provided buffer HEXKEYGRIP which must be of at
+ * least 41 bytes. If R_PKEY is not NULL and the function succeeded,
+ * the S-expression representing the key is stored there. The caller
+ * needs to call gcry_sexp_release on that. If R_ALGO is not NULL the
+ * public key algorithm id of Libgcrypt is stored there. */
+gpg_error_t
+app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip,
+ gcry_sexp_t *r_pkey, int *r_algo)
+{
+ gpg_error_t err;
+ ksba_sexp_t p;
+ size_t n;
+
+ if (r_pkey)
+ *r_pkey = NULL;
+
+ p = ksba_cert_get_public_key (cert);
+ if (!p)
+ return gpg_error (GPG_ERR_BUG);
+ n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip,
+ r_pkey, r_algo, NULL);
+ ksba_free (p);
+ return err;
+}
+
+
+/* Get the public key from the binary encoded (CERT,CERTLEN). */
+gpg_error_t
+app_help_pubkey_from_cert (const void *cert, size_t certlen,
+ unsigned char **r_pk, size_t *r_pklen)
+{
+ gpg_error_t err;
+ ksba_cert_t kc;
+ unsigned char *pk, *fixed_pk;
+ size_t pklen, fixed_pklen;
+
+ *r_pk = NULL;
+ *r_pklen = 0;
+
+ pk = NULL; /*(avoid cc warning)*/
+
+ err = ksba_cert_new (&kc);
+ if (err)
+ return err;
+
+ err = ksba_cert_init_from_mem (kc, cert, certlen);
+ if (err)
+ goto leave;
+
+ pk = ksba_cert_get_public_key (kc);
+ if (!pk)
+ {
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+ pklen = gcry_sexp_canon_len (pk, 0, NULL, &err);
+
+ err = uncompress_ecc_q_in_canon_sexp (pk, pklen, &fixed_pk, &fixed_pklen);
+ if (err)
+ goto leave;
+ if (fixed_pk)
+ {
+ ksba_free (pk); pk = NULL;
+ pk = fixed_pk;
+ pklen = fixed_pklen;
+ }
+
+ leave:
+ if (!err)
+ {
+ *r_pk = pk;
+ *r_pklen = pklen;
+ }
+ else
+ ksba_free (pk);
+ ksba_cert_release (kc);
+ return err;
+}
+
+
+/* Given the SLOT and the File ID FID, return the length of the
+ certificate contained in that file. Returns 0 if the file does not
+ exists or does not contain a certificate. If R_CERTOFF is not
+ NULL, the length the header will be stored at this address; thus to
+ parse the X.509 certificate a read should start at that offset.
+
+ On success the file is still selected.
+*/
+size_t
+app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ const unsigned char *p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t resultlen, objlen, hdrlen;
+
+ err = iso7816_select_file (slot, fid, 0);
+ if (err)
+ {
+ log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
+ return 0;
+ }
+
+ err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen);
+ if (err)
+ {
+ log_info ("error reading certificate from FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return 0;
+ }
+
+ if (!buflen || *buffer == 0xff)
+ {
+ log_info ("no certificate contained in FID 0x%04X\n", fid);
+ xfree (buffer);
+ return 0;
+ }
+
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ {
+ log_info ("error parsing certificate in FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ xfree (buffer);
+ return 0;
+ }
+
+ /* All certificates should commence with a SEQUENCE except for the
+ special ROOT CA which are enclosed in a SET. */
+ if ( !(class == CLASS_UNIVERSAL && constructed
+ && (tag == TAG_SEQUENCE || tag == TAG_SET)))
+ {
+ log_info ("data at FID 0x%04X does not look like a certificate\n", fid);
+ return 0;
+ }
+
+ resultlen = objlen + hdrlen;
+ if (r_certoff)
+ {
+ /* The callers want the offset to the actual certificate. */
+ *r_certoff = hdrlen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ return 0;
+
+ if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
+ {
+ /* The certificate seems to be contained in a
+ userCertificate container. Assume the following sequence
+ is the certificate. */
+ *r_certoff += hdrlen + objlen;
+ if (*r_certoff > resultlen)
+ {
+ *r_certoff = 0;
+ return 0; /* That should never happen. */
+ }
+ }
+ else
+ *r_certoff = 0;
+ }
+
+ return resultlen;
+}
diff --git a/scd/app-nks.c b/scd/app-nks.c
new file mode 100644
index 0000000..1f59321
--- /dev/null
+++ b/scd/app-nks.c
@@ -0,0 +1,1428 @@
+/* app-nks.c - The Telesec NKS card application.
+ * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Notes:
+
+ - We are now targeting TCOS 3 cards and it may happen that there is
+ a regression towards TCOS 2 cards. Please report.
+
+ - The TKS3 AUT key is not used. It seems that it is only useful for
+ the internal authentication command and not accessible by other
+ applications. The key itself is in the encryption class but the
+ corresponding certificate has only the digitalSignature
+ capability.
+
+ - If required, we automagically switch between the NKS application
+ and the SigG application. This avoids to use the DINSIG
+ application which is somewhat limited, has no support for Secure
+ Messaging as required by TCOS 3 and has no way to change the PIN
+ or even set the NullPIN.
+
+ - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer
+ cards. This is because the NKS application has moved to DF02 with
+ TCOS 3 and thus we better use a DF independent tag.
+
+ - We use only the global PINs for the NKS application.
+
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "scdaemon.h"
+#include "../common/i18n.h"
+#include "iso7816.h"
+#include "../common/tlv.h"
+#include "apdu.h"
+#include "../common/host2net.h"
+
+static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
+static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
+
+
+static struct
+{
+ int is_sigg; /* Valid for SigG application. */
+ int fid; /* File ID. */
+ int nks_ver; /* 0 for NKS version 2, 3 for version 3. */
+ int certtype; /* Type of certificate or 0 if it is not a certificate. */
+ int iskeypair; /* If true has the FID of the corresponding certificate. */
+ int issignkey; /* True if file is a key usable for signing. */
+ int isenckey; /* True if file is a key usable for decryption. */
+ unsigned char kid; /* Corresponding key references. */
+} filelist[] = {
+ { 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */
+ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
+ { 0, 0x4331, 0, 100 },
+ { 0, 0x4332, 0, 100 },
+ { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
+ { 0, 0x45B1, 0, 0, 0xC200, 0, 1, 0x81 }, /* EF_PK.NKS.ENC */
+ { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
+ { 0, 0x43B1, 0, 100 },
+ { 0, 0x43B2, 0, 100 },
+/* The authentication key is not used. */
+/* { 0, 0x4571, 3, 0, 0xC500, 0, 0, 0x82 }, /\* EF_PK.NKS.AUT *\/ */
+/* { 0, 0xC500, 3, 101 }, /\* EF_C.NKS.AUT *\/ */
+ { 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */
+ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
+ { 1, 0x4531, 3, 0, 0xC000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */
+ { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
+ { 1, 0xC008, 3, 101 }, /* EF_C.CA.SIG */
+ { 1, 0xC00E, 3, 111 }, /* EF_C.RCA.SIG */
+ { 0, 0 }
+};
+
+
+
+/* Object with application (i.e. NKS) specific data. */
+struct app_local_s {
+ int nks_version; /* NKS version. */
+
+ int sigg_active; /* True if switched to the SigG application. */
+ int sigg_msig_checked;/* True if we checked for a mass signature card. */
+ int sigg_is_msig; /* True if this is a mass signature card. */
+
+ int need_app_select; /* Need to re-select the application. */
+
+};
+
+
+
+static gpg_error_t switch_application (app_t app, int enable_sigg);
+
+
+
+/* Release local data. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+static int
+all_zero_p (void *buffer, size_t length)
+{
+ char *p;
+
+ for (p=buffer; length; length--, p++)
+ if (*p)
+ return 0;
+ return 1;
+}
+
+
+/* Read the file with FID, assume it contains a public key and return
+ its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
+static gpg_error_t
+keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
+{
+ gpg_error_t err;
+ unsigned char grip[20];
+ unsigned char *buffer[2];
+ size_t buflen[2];
+ gcry_sexp_t sexp;
+ int i;
+ int offset[2] = { 0, 0 };
+
+ err = iso7816_select_file (app->slot, fid, 0);
+ if (err)
+ return err;
+ err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]);
+ if (err)
+ return err;
+ err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]);
+ if (err)
+ {
+ xfree (buffer[0]);
+ return err;
+ }
+
+ if (app->app_local->nks_version < 3)
+ {
+ /* Old versions of NKS store the values in a TLV encoded format.
+ We need to do some checks. */
+ for (i=0; i < 2; i++)
+ {
+ /* Check that the value appears like an integer encoded as
+ Simple-TLV. We don't check the tag because the tests cards I
+ have use 1 for both, the modulus and the exponent - the
+ example in the documentation gives 2 for the exponent. */
+ if (buflen[i] < 3)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (buffer[i][1] != buflen[i]-2 )
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else
+ offset[i] = 2;
+ }
+ }
+ else
+ {
+ /* Remove leading zeroes to get a correct keygrip. Take care of
+ negative numbers. We should also fix it the same way in
+ libgcrypt but we can't yet rely on it yet. */
+ for (i=0; i < 2; i++)
+ {
+ while (buflen[i]-offset[i] > 1
+ && !buffer[i][offset[i]]
+ && !(buffer[i][offset[i]+1] & 0x80))
+ offset[i]++;
+ }
+ }
+
+ /* Check whether negative values are not prefixed with a zero and
+ fix that. */
+ for (i=0; i < 2; i++)
+ {
+ if ((buflen[i]-offset[i]) && (buffer[i][offset[i]] & 0x80))
+ {
+ unsigned char *newbuf;
+ size_t newlen;
+
+ newlen = 1 + buflen[i] - offset[i];
+ newbuf = xtrymalloc (newlen);
+ if (!newbuf)
+ {
+ xfree (buffer[0]);
+ xfree (buffer[1]);
+ return gpg_error_from_syserror ();
+ }
+ newbuf[0] = 0;
+ memcpy (newbuf+1, buffer[i]+offset[i], buflen[i] - offset[i]);
+ xfree (buffer[i]);
+ buffer[i] = newbuf;
+ buflen[i] = newlen;
+ offset[i] = 0;
+ }
+ }
+
+ if (!err)
+ err = gcry_sexp_build (&sexp, NULL,
+ "(public-key (rsa (n %b) (e %b)))",
+ (int)buflen[0]-offset[0], buffer[0]+offset[0],
+ (int)buflen[1]-offset[1], buffer[1]+offset[1]);
+
+ xfree (buffer[0]);
+ xfree (buffer[1]);
+ if (err)
+ return err;
+
+ if (!gcry_pk_get_keygrip (sexp, grip))
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by
+ libgcrypt. */
+ }
+ else
+ {
+ bin2hex (grip, 20, r_gripstr);
+ }
+ gcry_sexp_release (sexp);
+ return err;
+}
+
+
+/* TCOS responds to a verify with empty data (i.e. without the Lc
+ * byte) with the status of the PIN. PWID is the PIN ID, If SIGG is
+ * true, the application is switched into SigG mode. Returns:
+ * ISO7816_VERIFY_* codes or non-negative number of verification
+ * attempts left. */
+static int
+get_chv_status (app_t app, int sigg, int pwid)
+{
+ if (switch_application (app, sigg))
+ return sigg? -2 : -1; /* No such PIN / General error. */
+
+ return iso7816_verify_status (app_get_slot (app), pwid);
+}
+
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ static struct {
+ const char *name;
+ int special;
+ } table[] = {
+ { "$AUTHKEYID", 1 },
+ { "$ENCRKEYID", 2 },
+ { "$SIGNKEYID", 3 },
+ { "NKS-VERSION", 4 },
+ { "CHV-STATUS", 5 },
+ { NULL, 0 }
+ };
+ gpg_error_t err = 0;
+ int idx;
+ char buffer[100];
+
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+
+ switch (table[idx].special)
+ {
+ case 1: /* $AUTHKEYID */
+ {
+ /* NetKey 3.0 cards define an authentication key but according
+ to the specs this key is only usable for encryption and not
+ signing. it might work anyway but it has not yet been
+ tested - fixme. Thus for now we use the NKS signature key
+ for authentication. */
+ char const tmp[] = "NKS-NKS3.4531";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 2: /* $ENCRKEYID */
+ {
+ char const tmp[] = "NKS-NKS3.45B1";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 3: /* $SIGNKEYID */
+ {
+ char const tmp[] = "NKS-NKS3.4531";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 4: /* NKS-VERSION */
+ snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version);
+ send_status_info (ctrl, table[idx].name,
+ buffer, strlen (buffer), NULL, 0);
+ break;
+
+ case 5: /* CHV-STATUS */
+ {
+ /* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the
+ two global passwords followed by the two SigG passwords.
+ For the values, see the function get_chv_status. */
+ int tmp[4];
+
+ /* We use a helper array so that we can control that there is
+ no superfluous application switch. Note that PW2.CH.SIG
+ really has the identifier 0x83 and not 0x82 as one would
+ expect. */
+ tmp[0] = get_chv_status (app, 0, 0x00);
+ tmp[1] = get_chv_status (app, 0, 0x01);
+ tmp[2] = get_chv_status (app, 1, 0x81);
+ tmp[3] = get_chv_status (app, 1, 0x83);
+ snprintf (buffer, sizeof buffer,
+ "%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]);
+ send_status_info (ctrl, table[idx].name,
+ buffer, strlen (buffer), NULL, 0);
+ }
+ break;
+
+
+ default:
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ break;
+ }
+
+ return err;
+}
+
+
+
+static void
+do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags, int is_sigg)
+{
+ gpg_error_t err;
+ char ct_buf[100], id_buf[100];
+ int i;
+ const char *tag;
+ const char *usage;
+
+ if (is_sigg)
+ tag = "SIGG";
+ else if (app->app_local->nks_version < 3)
+ tag = "DF01";
+ else
+ tag = "NKS3";
+
+ /* Output information about all useful objects in the NKS application. */
+ for (i=0; filelist[i].fid; i++)
+ {
+ if (filelist[i].nks_ver > app->app_local->nks_version)
+ continue;
+
+ if (!!filelist[i].is_sigg != !!is_sigg)
+ continue;
+
+ if (filelist[i].certtype && !(flags &1))
+ {
+ size_t len;
+
+ len = app_help_read_length_of_cert (app->slot,
+ filelist[i].fid, NULL);
+ if (len)
+ {
+ /* FIXME: We should store the length in the application's
+ context so that a following readcert does only need to
+ read that many bytes. */
+ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
+ snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
+ tag, filelist[i].fid);
+ send_status_info (ctrl, "CERTINFO",
+ ct_buf, strlen (ct_buf),
+ id_buf, strlen (id_buf),
+ NULL, (size_t)0);
+ }
+ }
+ else if (filelist[i].iskeypair)
+ {
+ char gripstr[40+1];
+
+ err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
+ if (err)
+ log_error ("can't get keygrip from FID 0x%04X: %s\n",
+ filelist[i].fid, gpg_strerror (err));
+ else
+ {
+ snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
+ tag, filelist[i].fid);
+ if (filelist[i].issignkey && filelist[i].isenckey)
+ usage = "sae";
+ else if (filelist[i].issignkey)
+ usage = "sa";
+ else if (filelist[i].isenckey)
+ usage = "e";
+ else
+ usage = "";
+
+ send_status_info (ctrl, "KEYPAIRINFO",
+ gripstr, 40,
+ id_buf, strlen (id_buf),
+ usage, strlen (usage),
+ NULL, (size_t)0);
+ }
+ }
+ }
+
+
+}
+
+
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err;
+
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ do_learn_status_core (app, ctrl, flags, 0);
+
+ err = switch_application (app, 1);
+ if (err)
+ return 0; /* Silently ignore if we can't switch to SigG. */
+
+ do_learn_status_core (app, ctrl, flags, 1);
+
+ return 0;
+}
+
+
+
+
+/* Read the certificate with id CERTID (as returned by learn_status in
+ the CERTINFO status lines) and return it in the freshly allocated
+ buffer put into CERT and the length of the certificate put into
+ CERTLEN. */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen)
+{
+ int i, fid;
+ gpg_error_t err;
+ unsigned char *buffer;
+ const unsigned char *p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t totobjlen, objlen, hdrlen;
+ int rootca = 0;
+ int is_sigg = 0;
+
+ *cert = NULL;
+ *certlen = 0;
+
+ if (!strncmp (certid, "NKS-NKS3.", 9))
+ ;
+ else if (!strncmp (certid, "NKS-DF01.", 9))
+ ;
+ else if (!strncmp (certid, "NKS-SIGG.", 9))
+ is_sigg = 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
+ certid += 9;
+ if (!hexdigitp (certid) || !hexdigitp (certid+1)
+ || !hexdigitp (certid+2) || !hexdigitp (certid+3)
+ || certid[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (certid);
+ for (i=0; filelist[i].fid; i++)
+ if ((filelist[i].certtype || filelist[i].iskeypair)
+ && filelist[i].fid == fid)
+ break;
+ if (!filelist[i].fid)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ /* If the requested objects is a plain public key, redirect it to
+ the corresponding certificate. The whole system is a bit messy
+ because we sometime use the key directly or let the caller
+ retrieve the key from the certificate. The rationale for
+ that is to support not-yet stored certificates. */
+ if (filelist[i].iskeypair)
+ fid = filelist[i].iskeypair;
+
+
+ /* Read the entire file. fixme: This could be optimized by first
+ reading the header to figure out how long the certificate
+ actually is. */
+ err = iso7816_select_file (app->slot, fid, 0);
+ if (err)
+ {
+ log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
+ if (err)
+ {
+ log_error ("error reading certificate from FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return err;
+ }
+
+ if (!buflen || *buffer == 0xff)
+ {
+ log_info ("no certificate contained in FID 0x%04X\n", fid);
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+
+ /* Now figure something out about the object. */
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
+ ;
+ else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
+ rootca = 1;
+ else
+ return gpg_error (GPG_ERR_INV_OBJ);
+ totobjlen = objlen + hdrlen;
+ assert (totobjlen <= buflen);
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (rootca)
+ ;
+ else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
+ {
+ const unsigned char *save_p;
+
+ /* The certificate seems to be contained in a userCertificate
+ container. Skip this and assume the following sequence is
+ the certificate. */
+ if (n < objlen)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ p += objlen;
+ n -= objlen;
+ save_p = p;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
+ return gpg_error (GPG_ERR_INV_OBJ);
+ totobjlen = objlen + hdrlen;
+ assert (save_p + totobjlen <= buffer + buflen);
+ memmove (buffer, save_p, totobjlen);
+ }
+
+ *cert = buffer;
+ buffer = NULL;
+ *certlen = totobjlen;
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Handle the READKEY command. On success a canonical encoded
+ S-expression with the public key will get stored at PK and its
+ length at PKLEN; the caller must release that buffer. On error PK
+ and PKLEN are not changed and an error code is returned. As of now
+ this function is only useful for the internal authentication key.
+ Other keys are automagically retrieved via by means of the
+ certificate parsing code in commands.c:cmd_readkey. For internal
+ use PK and PKLEN may be NULL to just check for an existing key. */
+static gpg_error_t
+do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
+ unsigned char **pk, size_t *pklen)
+{
+ gpg_error_t err;
+ unsigned char *buffer[2];
+ size_t buflen[2];
+ unsigned short path[1] = { 0x4500 };
+
+ (void)ctrl;
+
+ if ((flags & APP_READKEY_FLAG_ADVANCED))
+ return GPG_ERR_NOT_SUPPORTED;
+
+ /* We use a generic name to retrieve PK.AUT.IFD-SPK. */
+ if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3)
+ ;
+ else /* Return the error code expected by cmd_readkey. */
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* Access the KEYD file which is always in the master directory. */
+ err = iso7816_select_path (app_get_slot (app), path, DIM (path), 0);
+ if (err)
+ return err;
+ /* Due to the above select we need to re-select our application. */
+ app->app_local->need_app_select = 1;
+ /* Get the two records. */
+ err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]);
+ if (err)
+ return err;
+ if (all_zero_p (buffer[0], buflen[0]))
+ {
+ xfree (buffer[0]);
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ }
+ err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]);
+ if (err)
+ {
+ xfree (buffer[0]);
+ return err;
+ }
+
+ if (pk && pklen)
+ {
+ *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0],
+ buffer[1], buflen[1],
+ pklen);
+ if (!*pk)
+ err = gpg_error_from_syserror ();
+ }
+
+ xfree (buffer[0]);
+ xfree (buffer[1]);
+ return err;
+}
+
+
+/* Handle the WRITEKEY command for NKS. This function expects a
+ canonical encoded S-expression with the public key in KEYDATA and
+ its length in KEYDATALEN. The only supported KEYID is
+ "$IFDAUTHKEY" to store the terminal key on the card. Bit 0 of
+ FLAGS indicates whether an existing key shall get overwritten.
+ PINCB and PINCB_ARG are the usual arguments for the pinentry
+ callback. */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+ int force = (flags & 1);
+ const unsigned char *rsa_n = NULL;
+ const unsigned char *rsa_e = NULL;
+ size_t rsa_n_len, rsa_e_len;
+ unsigned int nbits;
+
+ (void)pincb;
+ (void)pincb_arg;
+
+ if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3)
+ ;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL))
+ return gpg_error (GPG_ERR_EEXIST);
+
+ /* Parse the S-expression. */
+ err = get_rsa_pk_from_canon_sexp (keydata, keydatalen,
+ &rsa_n, &rsa_n_len, &rsa_e, &rsa_e_len);
+ if (err)
+ goto leave;
+
+ /* Check that the parameters match the requirements. */
+ nbits = app_help_count_bits (rsa_n, rsa_n_len);
+ if (nbits != 1024)
+ {
+ log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ goto leave;
+ }
+
+ nbits = app_help_count_bits (rsa_e, rsa_e_len);
+ if (nbits < 2 || nbits > 32)
+ {
+ log_error (_("RSA public exponent missing or larger than %d bits\n"),
+ 32);
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ goto leave;
+ }
+
+/* /\* Store them. *\/ */
+/* err = verify_pin (app, 0, NULL, pincb, pincb_arg); */
+/* if (err) */
+/* goto leave; */
+
+ /* Send the MSE:Store_Public_Key. */
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+/* mse = xtrymalloc (1000); */
+
+/* mse[0] = 0x80; /\* Algorithm reference. *\/ */
+/* mse[1] = 1; */
+/* mse[2] = 0x17; */
+/* mse[3] = 0x84; /\* Private key reference. *\/ */
+/* mse[4] = 1; */
+/* mse[5] = 0x77; */
+/* mse[6] = 0x7F; /\* Public key parameter. *\/ */
+/* mse[7] = 0x49; */
+/* mse[8] = 0x81; */
+/* mse[9] = 3 + 0x80 + 2 + rsa_e_len; */
+/* mse[10] = 0x81; /\* RSA modulus of 128 byte. *\/ */
+/* mse[11] = 0x81; */
+/* mse[12] = rsa_n_len; */
+/* memcpy (mse+12, rsa_n, rsa_n_len); */
+/* mse[10] = 0x82; /\* RSA public exponent of up to 4 bytes. *\/ */
+/* mse[12] = rsa_e_len; */
+/* memcpy (mse+12, rsa_e, rsa_e_len); */
+/* err = iso7816_manage_security_env (app->slot, 0x81, 0xB6, */
+/* mse, sizeof mse); */
+
+ leave:
+ return err;
+}
+
+
+static gpg_error_t
+basic_pin_checks (const char *pinvalue, int minlen, int maxlen)
+{
+ if (strlen (pinvalue) < minlen)
+ {
+ log_error ("PIN is too short; minimum length is %d\n", minlen);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ if (strlen (pinvalue) > maxlen)
+ {
+ log_error ("PIN is too large; maximum length is %d\n", maxlen);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ return 0;
+}
+
+
+/* Verify the PIN if required. */
+static gpg_error_t
+verify_pin (app_t app, int pwid, const char *desc,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ pininfo_t pininfo;
+ int rc;
+
+ if (!desc)
+ desc = "PIN";
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = 6;
+ pininfo.maxlen = 16;
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
+ {
+ rc = pincb (pincb_arg, desc, NULL);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = iso7816_verify_kp (app->slot, pwid, &pininfo);
+ pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+ }
+ else
+ {
+ char *pinvalue;
+
+ rc = pincb (pincb_arg, desc, &pinvalue);
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
+ if (rc)
+ {
+ xfree (pinvalue);
+ return rc;
+ }
+
+ rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ }
+
+ if (rc)
+ {
+ if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS )
+ log_error (_("the NullPIN has not yet been changed\n"));
+ else
+ log_error ("verify PIN failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN;
+ that callback should return the PIN in an allocated buffer and
+ store that in the 3rd argument. */
+static gpg_error_t
+do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ int rc, i;
+ int is_sigg = 0;
+ int fid;
+ unsigned char kid;
+ unsigned char data[83]; /* Must be large enough for a SHA-1 digest
+ + the largest OID prefix. */
+ size_t datalen;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ switch (indatalen)
+ {
+ case 16: case 20: case 35: case 47: case 51: case 67: case 83: break;
+ default: return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ /* Check that the provided ID is valid. This is not really needed
+ but we do it to enforce correct usage by the caller. */
+ if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
+ is_sigg = 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+ keyidstr += 9;
+
+ rc = switch_application (app, is_sigg);
+ if (rc)
+ return rc;
+
+ if (is_sigg && app->app_local->sigg_is_msig)
+ {
+ log_info ("mass signature cards are not allowed\n");
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+ }
+
+ if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
+ || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
+ || keyidstr[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (keyidstr);
+ for (i=0; filelist[i].fid; i++)
+ if (filelist[i].iskeypair && filelist[i].fid == fid)
+ break;
+ if (!filelist[i].fid)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ if (!filelist[i].issignkey)
+ return gpg_error (GPG_ERR_INV_ID);
+ kid = filelist[i].kid;
+
+ /* Prepare the DER object from INDATA. */
+ if (app->app_local->nks_version > 2 && (indatalen == 35
+ || indatalen == 47
+ || indatalen == 51
+ || indatalen == 67
+ || indatalen == 83))
+ {
+ /* The caller send data matching the length of the ASN.1 encoded
+ hash for SHA-{1,224,256,384,512}. Assume that is okay. */
+ assert (indatalen <= sizeof data);
+ memcpy (data, indata, indatalen);
+ datalen = indatalen;
+ }
+ else if (indatalen == 35)
+ {
+ /* Alright, the caller was so kind to send us an already
+ prepared DER object. This is for TCOS 2. */
+ if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
+ ;
+ else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata,rmd160_prefix,15))
+ ;
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data, indata, indatalen);
+ datalen = 35;
+ }
+ else if (indatalen == 20)
+ {
+ if (hashalgo == GCRY_MD_SHA1)
+ memcpy (data, sha1_prefix, 15);
+ else if (hashalgo == GCRY_MD_RMD160)
+ memcpy (data, rmd160_prefix, 15);
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data+15, indata, indatalen);
+ datalen = 35;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+
+ /* Send an MSE for PSO:Computer_Signature. */
+ if (app->app_local->nks_version > 2)
+ {
+ unsigned char mse[6];
+
+ mse[0] = 0x80; /* Algorithm reference. */
+ mse[1] = 1;
+ mse[2] = 2; /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check. */
+ mse[3] = 0x84; /* Private key reference. */
+ mse[4] = 1;
+ mse[5] = kid;
+ rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6,
+ mse, sizeof mse);
+ }
+ /* Verify using PW1.CH. */
+ if (!rc)
+ rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+ /* Compute the signature. */
+ if (!rc)
+ rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0,
+ outdata, outdatalen);
+ return rc;
+}
+
+
+
+/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+static gpg_error_t
+do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info)
+{
+ int rc, i;
+ int is_sigg = 0;
+ int fid;
+ int kid;
+
+ (void)ctrl;
+ (void)r_info;
+
+ if (!keyidstr || !*keyidstr || !indatalen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check that the provided ID is valid. This is not really needed
+ but we do it to enforce correct usage by the caller. */
+ if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
+ is_sigg = 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+ keyidstr += 9;
+
+ rc = switch_application (app, is_sigg);
+ if (rc)
+ return rc;
+
+ if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
+ || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
+ || keyidstr[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (keyidstr);
+ for (i=0; filelist[i].fid; i++)
+ if (filelist[i].iskeypair && filelist[i].fid == fid)
+ break;
+ if (!filelist[i].fid)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ if (!filelist[i].isenckey)
+ return gpg_error (GPG_ERR_INV_ID);
+ kid = filelist[i].kid;
+
+ if (app->app_local->nks_version > 2)
+ {
+ unsigned char mse[6];
+ mse[0] = 0x80; /* Algorithm reference. */
+ mse[1] = 1;
+ mse[2] = 0x0a; /* RSA no padding. (0x1A is pkcs#1.5 padding.) */
+ mse[3] = 0x84; /* Private key reference. */
+ mse[4] = 1;
+ mse[5] = kid;
+ rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8,
+ mse, sizeof mse);
+ }
+ else
+ {
+ static const unsigned char mse[] =
+ {
+ 0x80, 1, 0x10, /* Select algorithm RSA. */
+ 0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
+ };
+ rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8,
+ mse, sizeof mse);
+
+ }
+
+ if (!rc)
+ rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+
+ /* Note that we need to use extended length APDUs for TCOS 3 cards.
+ Command chaining does not work. */
+ if (!rc)
+ rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0,
+ indata, indatalen, 0, 0x81,
+ outdata, outdatalen);
+ return rc;
+}
+
+
+
+/* Parse a password ID string. Returns NULL on error or a string
+ suitable as passphrase prompt on success. On success stores the
+ reference value for the password at R_PWID and a flag indicating
+ that the SigG application is to be used at R_SIGG. If NEW_MODE is
+ true, the returned description is suitable for a new Password.
+ Supported values for PWIDSTR are:
+
+ PW1.CH - Global password 1
+ PW2.CH - Global password 2
+ PW1.CH.SIG - SigG password 1
+ PW2.CH.SIG - SigG password 2
+ */
+static const char *
+parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid)
+{
+ const char *desc;
+
+ if (!pwidstr)
+ desc = NULL;
+ else if (!strcmp (pwidstr, "PW1.CH"))
+ {
+ *r_sigg = 0;
+ *r_pwid = 0x00;
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but keep
+ them verbatim at the start of the string. */
+ desc = (new_mode
+ ? _("|N|Please enter a new PIN for the standard keys.")
+ : _("||Please enter the PIN for the standard keys."));
+ }
+ else if (!strcmp (pwidstr, "PW2.CH"))
+ {
+ *r_pwid = 0x01;
+ desc = (new_mode
+ ? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
+ "for the standard keys.")
+ : _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the standard keys."));
+ }
+ else if (!strcmp (pwidstr, "PW1.CH.SIG"))
+ {
+ *r_pwid = 0x81;
+ *r_sigg = 1;
+ desc = (new_mode
+ ? _("|N|Please enter a new PIN for the key to create "
+ "qualified signatures.")
+ : _("||Please enter the PIN for the key to create "
+ "qualified signatures."));
+ }
+ else if (!strcmp (pwidstr, "PW2.CH.SIG"))
+ {
+ *r_pwid = 0x83; /* Yes, that is 83 and not 82. */
+ *r_sigg = 1;
+ desc = (new_mode
+ ? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
+ "for the key to create qualified signatures.")
+ : _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the key to create qualified signatures."));
+ }
+ else
+ {
+ *r_pwid = 0; /* Only to avoid gcc warning in calling function. */
+ desc = NULL; /* Error. */
+ }
+
+ return desc;
+}
+
+
+/* Handle the PASSWD command. See parse_pwidstr() for allowed values
+ for CHVNOSTR. */
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
+ unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ char *newpin = NULL;
+ char *oldpin = NULL;
+ size_t newpinlen;
+ size_t oldpinlen;
+ int is_sigg;
+ const char *newdesc;
+ int pwid;
+ pininfo_t pininfo;
+
+ (void)ctrl;
+
+ /* The minimum length is enforced by TCOS, the maximum length is
+ just a reasonable value. */
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.minlen = 6;
+ pininfo.maxlen = 16;
+
+ newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &pwid);
+ if (!newdesc)
+ return gpg_error (GPG_ERR_INV_ID);
+
+ if ((flags & APP_CHANGE_FLAG_CLEAR))
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
+ if ((flags & APP_CHANGE_FLAG_NULLPIN))
+ {
+ /* With the nullpin flag, we do not verify the PIN - it would
+ fail if the Nullpin is still set. */
+ oldpin = xtrycalloc (1, 6);
+ if (!oldpin)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ oldpinlen = 6;
+ }
+ else
+ {
+ const char *desc;
+ int dummy1, dummy2;
+
+ if ((flags & APP_CHANGE_FLAG_RESET))
+ {
+ /* Reset mode: Ask for the alternate PIN. */
+ const char *altpwidstr;
+
+ if (!strcmp (pwidstr, "PW1.CH"))
+ altpwidstr = "PW2.CH";
+ else if (!strcmp (pwidstr, "PW2.CH"))
+ altpwidstr = "PW1.CH";
+ else if (!strcmp (pwidstr, "PW1.CH.SIG"))
+ altpwidstr = "PW2.CH.SIG";
+ else if (!strcmp (pwidstr, "PW2.CH.SIG"))
+ altpwidstr = "PW1.CH.SIG";
+ else
+ {
+ err = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2);
+ }
+ else
+ {
+ /* Regular change mode: Ask for the old PIN. */
+ desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2);
+ }
+ err = pincb (pincb_arg, desc, &oldpin);
+ if (err)
+ {
+ log_error ("error getting old PIN: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ oldpinlen = strlen (oldpin);
+ err = basic_pin_checks (oldpin, pininfo.minlen, pininfo.maxlen);
+ if (err)
+ goto leave;
+ }
+
+ err = pincb (pincb_arg, newdesc, &newpin);
+ if (err)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ newpinlen = strlen (newpin);
+
+ err = basic_pin_checks (newpin, pininfo.minlen, pininfo.maxlen);
+ if (err)
+ goto leave;
+
+ if ((flags & APP_CHANGE_FLAG_RESET))
+ {
+ char *data;
+ size_t datalen = oldpinlen + newpinlen;
+
+ data = xtrymalloc (datalen);
+ if (!data)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (data, oldpin, oldpinlen);
+ memcpy (data+oldpinlen, newpin, newpinlen);
+ err = iso7816_reset_retry_counter_with_rc (app->slot, pwid,
+ data, datalen);
+ wipememory (data, datalen);
+ xfree (data);
+ }
+ else
+ err = iso7816_change_reference_data (app->slot, pwid,
+ oldpin, oldpinlen,
+ newpin, newpinlen);
+ leave:
+ xfree (oldpin);
+ xfree (newpin);
+ return err;
+}
+
+
+/* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
+static gpg_error_t
+do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ int pwid;
+ int is_sigg;
+ const char *desc;
+
+ (void)ctrl;
+
+ desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid);
+ if (!desc)
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
+ return verify_pin (app, pwid, desc, pincb, pincb_arg);
+}
+
+
+/* Return the version of the NKS application. */
+static int
+get_nks_version (int slot)
+{
+ unsigned char *result = NULL;
+ size_t resultlen;
+ int type;
+
+ if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0,
+ NULL, &result, &resultlen))
+ return 2; /* NKS 2 does not support this command. */
+
+ /* Example value: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00
+ vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx
+ vendor (Philips) -+ | | | | | | |
+ chip type -----------+ | | | | | |
+ chip id ----------------+ | | | | |
+ card type (3 - tcos 3) -------------------+ | | | |
+ OS version of card type ---------------------+ | | |
+ OS release of card type ------------------------+ | |
+ OS vendor internal version ------------------------+ |
+ RFU -----------------------------------------------------------+
+ */
+ if (resultlen < 16)
+ type = 0; /* Invalid data returned. */
+ else
+ type = result[8];
+ xfree (result);
+
+ return type;
+}
+
+
+/* If ENABLE_SIGG is true switch to the SigG application if not yet
+ active. If false switch to the NKS application if not yet active.
+ Returns 0 on success. */
+static gpg_error_t
+switch_application (app_t app, int enable_sigg)
+{
+ gpg_error_t err;
+
+ if (((app->app_local->sigg_active && enable_sigg)
+ || (!app->app_local->sigg_active && !enable_sigg))
+ && !app->app_local->need_app_select)
+ return 0; /* Already switched. */
+
+ log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS");
+ if (enable_sigg)
+ err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0);
+ else
+ err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0);
+
+ if (!err && enable_sigg && app->app_local->nks_version >= 3
+ && !app->app_local->sigg_msig_checked)
+ {
+ /* Check whether this card is a mass signature card. */
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *tmpl;
+ size_t tmpllen;
+
+ app->app_local->sigg_msig_checked = 1;
+ app->app_local->sigg_is_msig = 1;
+ err = iso7816_select_file (app->slot, 0x5349, 0);
+ if (!err)
+ err = iso7816_read_record (app->slot, 1, 1, 0, &buffer, &buflen);
+ if (!err)
+ {
+ tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen);
+ if (tmpl && tmpllen == 12
+ && !memcmp (tmpl,
+ "\x93\x02\x00\x01\xA4\x06\x83\x01\x81\x83\x01\x83",
+ 12))
+ app->app_local->sigg_is_msig = 0;
+ xfree (buffer);
+ }
+ if (app->app_local->sigg_is_msig)
+ log_info ("This is a mass signature card\n");
+ }
+
+ if (!err)
+ {
+ app->app_local->need_app_select = 0;
+ app->app_local->sigg_active = enable_sigg;
+ }
+ else
+ log_error ("app-nks: error switching to %s: %s\n",
+ enable_sigg? "SigG":"NKS", gpg_strerror (err));
+
+ return err;
+}
+
+
+/* Select the NKS application. */
+gpg_error_t
+app_select_nks (app_t app)
+{
+ int slot = app->slot;
+ int rc;
+
+ rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0);
+ if (!rc)
+ {
+ app->apptype = APPTYPE_NKS;
+
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
+
+ app->app_local->nks_version = get_nks_version (slot);
+ if (opt.verbose)
+ log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
+
+ app->fnc.deinit = do_deinit;
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = do_readcert;
+ app->fnc.readkey = do_readkey;
+ app->fnc.getattr = do_getattr;
+ app->fnc.setattr = NULL;
+ app->fnc.writekey = do_writekey;
+ app->fnc.genkey = NULL;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = NULL;
+ app->fnc.decipher = do_decipher;
+ app->fnc.change_pin = do_change_pin;
+ app->fnc.check_pin = do_check_pin;
+ }
+
+ leave:
+ if (rc)
+ do_deinit (app);
+ return rc;
+}
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
new file mode 100644
index 0000000..da6dc7a
--- /dev/null
+++ b/scd/app-openpgp.c
@@ -0,0 +1,5480 @@
+/* app-openpgp.c - The OpenPGP card application.
+ * Copyright (C) 2003-2005, 2007-2009,
+ * 2013-2015 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2005, 2007-2009, 2013-2015, 2020 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Some notes:
+
+ CHV means Card Holder Verification and is nothing else than a PIN
+ or password. That term seems to have been used originally with GSM
+ cards. Version v2 of the specs changes the term to the clearer
+ term PW for password. We use the terms here interchangeable
+ because we do not want to change existing strings i18n wise.
+
+ Version 2 of the specs also drops the separate PW2 which was
+ required in v1 due to ISO requirements. It is now possible to have
+ one physical PW but two reference to it so that they can be
+ individually be verified (e.g. to implement a forced verification
+ for one key). Thus you will noticed the use of PW2 with the verify
+ command but not with change_reference_data because the latter
+ operates directly on the physical PW.
+
+ The Reset Code (RC) as implemented by v2 cards uses the same error
+ counter as the PW2 of v1 cards. By default no RC is set and thus
+ that error counter is set to 0. After setting the RC the error
+ counter will be initialized to 3.
+
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "scdaemon.h"
+
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "iso7816.h"
+#include "../common/tlv.h"
+#include "../common/host2net.h"
+#include "../common/openpgpdefs.h"
+
+
+#define KDF_DATA_LENGTH_MIN 90
+#define KDF_DATA_LENGTH_MAX 110
+
+/* A table describing the DOs of the card. */
+static struct {
+ int tag;
+ int constructed;
+ int get_from; /* Constructed DO with this DO or 0 for direct access. */
+ unsigned int binary:1;
+ unsigned int dont_cache:1;
+ unsigned int flush_on_error:1;
+ unsigned int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
+ this data object if it is used in 1.1
+ and later versions of the card. This
+ does not work with composite DO and
+ is currently only useful for the CHV
+ status bytes. */
+ unsigned int try_extlen:2; /* Large object; try to use an extended
+ length APDU when !=0. The size is
+ determined by extcap.max_certlen_3
+ when == 1, and by extcap.max_special_do
+ when == 2. */
+ char *desc;
+} data_objects[] = {
+ { 0x005E, 0, 0, 1, 0, 0, 0, 2, "Login Data" },
+ { 0x5F50, 0, 0, 0, 0, 0, 0, 2, "URL" },
+ { 0x5F52, 0, 0, 1, 0, 0, 0, 0, "Historical Bytes" },
+ { 0x0065, 1, 0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
+ { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
+ { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
+ { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Salutation" },
+ { 0x006E, 1, 0, 1, 0, 0, 0, 0, "Application Related Data" },
+ { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
+ { 0x0073, 1, 0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
+ { 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" },
+ { 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" },
+ { 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" },
+ { 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" },
+ { 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" },
+ { 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" },
+ { 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" },
+ { 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" },
+ { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
+ { 0x007A, 1, 0, 1, 0, 0, 0, 0, "Security Support Template" },
+ { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
+ { 0x0101, 0, 0, 0, 0, 0, 0, 2, "Private DO 1"},
+ { 0x0102, 0, 0, 0, 0, 0, 0, 2, "Private DO 2"},
+ { 0x0103, 0, 0, 0, 0, 0, 0, 2, "Private DO 3"},
+ { 0x0104, 0, 0, 0, 0, 0, 0, 2, "Private DO 4"},
+ { 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"},
+ /* V3.0 */
+ { 0x7F74, 0, 0, 1, 0, 0, 0, 0, "General Feature Management"},
+ { 0x00D5, 0, 0, 1, 0, 0, 0, 0, "AES key data"},
+ { 0x00F9, 0, 0, 1, 0, 0, 0, 0, "KDF data object"},
+ { 0 }
+};
+
+
+/* Type of keys. */
+typedef enum
+ {
+ KEY_TYPE_ECC,
+ KEY_TYPE_RSA,
+ }
+key_type_t;
+
+
+/* The format of RSA private keys. */
+typedef enum
+ {
+ RSA_UNKNOWN_FMT,
+ RSA_STD,
+ RSA_STD_N,
+ RSA_CRT,
+ RSA_CRT_N
+ }
+rsa_key_format_t;
+
+
+/* One cache item for DOs. */
+struct cache_s {
+ struct cache_s *next;
+ int tag;
+ size_t length;
+ unsigned char data[1];
+};
+
+
+/* Object with application (i.e. OpenPGP card) specific data. */
+struct app_local_s {
+ /* A linked list with cached DOs. */
+ struct cache_s *cache;
+
+ /* Keep track of the public keys. */
+ struct
+ {
+ int read_done; /* True if we have at least tried to read them. */
+ unsigned char *key; /* This is a malloced buffer with a canonical
+ encoded S-expression encoding a public
+ key. Might be NULL if key is not
+ available. */
+ size_t keylen; /* The length of the above S-expression. This
+ is usually only required for cross checks
+ because the length of an S-expression is
+ implicitly available. */
+ unsigned char keygrip_str[41]; /* The keygrip, null terminated */
+ } pk[3];
+
+ unsigned char status_indicator; /* The card status indicator. */
+
+ unsigned int manufacturer:16; /* Manufacturer ID from the s/n. */
+
+ /* Keep track of the ISO card capabilities. */
+ struct
+ {
+ unsigned int cmd_chaining:1; /* Command chaining is supported. */
+ unsigned int ext_lc_le:1; /* Extended Lc and Le are supported. */
+ } cardcap;
+
+ /* Keep track of extended card capabilities. */
+ struct
+ {
+ unsigned int is_v2:1; /* Compatible to v2 or later. */
+ unsigned int extcap_v3:1; /* Extcap is in v3 format. */
+ unsigned int has_button:1; /* Has confirmation button or not. */
+
+ unsigned int sm_supported:1; /* Secure Messaging is supported. */
+ unsigned int get_challenge:1;
+ unsigned int key_import:1;
+ unsigned int change_force_chv:1;
+ unsigned int private_dos:1;
+ unsigned int algo_attr_change:1; /* Algorithm attributes changeable. */
+ unsigned int has_decrypt:1; /* Support symmetric decryption. */
+ unsigned int kdf_do:1; /* Support KDF DO. */
+
+ unsigned int sm_algo:2; /* Symmetric crypto algo for SM. */
+ unsigned int pin_blk2:1; /* PIN block 2 format supported. */
+ unsigned int mse:1; /* MSE command supported. */
+ unsigned int max_certlen_3:16;
+ unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */
+ unsigned int max_special_do:16; /* Maximum size for special DOs. */
+ } extcap;
+
+ /* Flags used to control the application. */
+ struct
+ {
+ unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */
+ unsigned int def_chv2:1; /* Use 123456 for CHV2. */
+ } flags;
+
+ /* Pinpad request specified on card. */
+ struct
+ {
+ unsigned int disabled:1; /* No pinpad use because of KDF DO. */
+ unsigned int specified:1;
+ int fixedlen_user;
+ int fixedlen_admin;
+ } pinpad;
+
+ struct
+ {
+ key_type_t key_type;
+ union {
+ struct {
+ unsigned int n_bits; /* Size of the modulus in bits. The rest
+ of this strucuire is only valid if
+ this is not 0. */
+ unsigned int e_bits; /* Size of the public exponent in bits. */
+ rsa_key_format_t format;
+ } rsa;
+ struct {
+ const char *curve;
+ int flags;
+ } ecc;
+ };
+ } keyattr[3];
+};
+
+#define ECC_FLAG_DJB_TWEAK (1 << 0)
+#define ECC_FLAG_PUBKEY (1 << 1)
+
+
+/***** Local prototypes *****/
+static unsigned long convert_sig_counter_value (const unsigned char *value,
+ size_t valuelen);
+static unsigned long get_sig_counter (app_t app);
+static gpg_error_t do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+static gpg_error_t parse_algorithm_attribute (app_t app, int keyno);
+static gpg_error_t change_keyattr_from_string
+ (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *value, size_t valuelen);
+
+
+/* Return the OpenPGP card manufacturer name. */
+static const char *
+get_manufacturer (unsigned int no)
+{
+ /* Note: Make sure that there is no colon or linefeed in the string. */
+ switch (no)
+ {
+ case 0x0001: return "PPC Card Systems";
+ case 0x0002: return "Prism";
+ case 0x0003: return "OpenFortress";
+ case 0x0004: return "Wewid";
+ case 0x0005: return "ZeitControl";
+ case 0x0006: return "Yubico";
+ case 0x0007: return "OpenKMS";
+ case 0x0008: return "LogoEmail";
+ case 0x0009: return "Fidesmo";
+ case 0x000A: return "VivoKey";
+ case 0x000B: return "Feitian Technologies";
+ case 0x000D: return "Dangerous Things";
+ case 0x000E: return "Excelsecu";
+
+ case 0x002A: return "Magrathea";
+ case 0x0042: return "GnuPG e.V.";
+
+ case 0x1337: return "Warsaw Hackerspace";
+ case 0x2342: return "warpzone"; /* hackerspace Muenster. */
+ case 0x4354: return "Confidential Technologies"; /* cotech.de */
+ case 0x5343: return "SSE Carte à puce";
+ case 0x5443: return "TIF-IT e.V.";
+ case 0x63AF: return "Trustica";
+ case 0xBA53: return "c-base e.V.";
+ case 0xBD0E: return "Paranoidlabs";
+ case 0xF517: return "FSIJ";
+ case 0xF5EC: return "F-Secure";
+
+ /* 0x0000 and 0xFFFF are defined as test cards per spec,
+ * 0xFF00 to 0xFFFE are assigned for use with randomly created
+ * serial numbers. */
+ case 0x0000:
+ case 0xffff: return "test card";
+ default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
+ }
+}
+
+
+
+
+/* Deconstructor. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ struct cache_s *c, *c2;
+ int i;
+
+ for (c = app->app_local->cache; c; c = c2)
+ {
+ c2 = c->next;
+ xfree (c);
+ }
+
+ for (i=0; i < DIM (app->app_local->pk); i++)
+ {
+ xfree (app->app_local->pk[i].key);
+ app->app_local->pk[i].read_done = 0;
+ }
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+/* Wrapper around iso7816_get_data which first tries to get the data
+ from the cache. With GET_IMMEDIATE passed as true, the cache is
+ bypassed. With TRY_EXTLEN extended lengths APDUs are use if
+ supported by the card. */
+static gpg_error_t
+get_cached_data (app_t app, int tag,
+ unsigned char **result, size_t *resultlen,
+ int get_immediate, int try_extlen)
+{
+ gpg_error_t err;
+ int i;
+ unsigned char *p;
+ size_t len;
+ struct cache_s *c;
+ int exmode;
+
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!get_immediate)
+ {
+ for (c=app->app_local->cache; c; c = c->next)
+ if (c->tag == tag)
+ {
+ if(c->length)
+ {
+ p = xtrymalloc (c->length);
+ if (!p)
+ return gpg_error_from_syserror ();
+ memcpy (p, c->data, c->length);
+ *result = p;
+ }
+
+ *resultlen = c->length;
+
+ return 0;
+ }
+ }
+
+ if (try_extlen && app->app_local->cardcap.ext_lc_le)
+ {
+ if (try_extlen == 1)
+ exmode = app->app_local->extcap.max_certlen_3;
+ else if (try_extlen == 2 && app->app_local->extcap.extcap_v3)
+ exmode = app->app_local->extcap.max_special_do;
+ else
+ exmode = 0;
+ }
+ else
+ exmode = 0;
+
+ err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
+ if (err)
+ return err;
+ if (len)
+ *result = p;
+ *resultlen = len;
+
+ /* Check whether we should cache this object. */
+ if (get_immediate)
+ return 0;
+
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].tag == tag)
+ {
+ if (data_objects[i].dont_cache)
+ return 0;
+ break;
+ }
+
+ /* Okay, cache it. */
+ for (c=app->app_local->cache; c; c = c->next)
+ assert (c->tag != tag);
+
+ c = xtrymalloc (sizeof *c + len);
+ if (c)
+ {
+ if (len)
+ memcpy (c->data, p, len);
+ else
+ xfree (p);
+ c->length = len;
+ c->tag = tag;
+ c->next = app->app_local->cache;
+ app->app_local->cache = c;
+ }
+
+ return 0;
+}
+
+/* Remove DO at TAG from the cache. */
+static void
+flush_cache_item (app_t app, int tag)
+{
+ struct cache_s *c, *cprev;
+ int i;
+
+ if (!app->app_local)
+ return;
+
+ for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
+ if (c->tag == tag)
+ {
+ if (cprev)
+ cprev->next = c->next;
+ else
+ app->app_local->cache = c->next;
+ xfree (c);
+
+ for (c=app->app_local->cache; c ; c = c->next)
+ {
+ assert (c->tag != tag); /* Oops: duplicated entry. */
+ }
+ return;
+ }
+
+ /* Try again if we have an outer tag. */
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].tag == tag && data_objects[i].get_from
+ && data_objects[i].get_from != tag)
+ flush_cache_item (app, data_objects[i].get_from);
+}
+
+/* Flush all entries from the cache which might be out of sync after
+ an error. */
+static void
+flush_cache_after_error (app_t app)
+{
+ int i;
+
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].flush_on_error)
+ flush_cache_item (app, data_objects[i].tag);
+}
+
+
+/* Flush the entire cache. */
+static void
+flush_cache (app_t app)
+{
+ if (app && app->app_local)
+ {
+ struct cache_s *c, *c2;
+
+ for (c = app->app_local->cache; c; c = c2)
+ {
+ c2 = c->next;
+ xfree (c);
+ }
+ app->app_local->cache = NULL;
+ }
+}
+
+
+/* Get the DO identified by TAG from the card in SLOT and return a
+ buffer with its content in RESULT and NBYTES. The return value is
+ NULL if not found or a pointer which must be used to release the
+ buffer holding value. */
+static void *
+get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
+ int *r_rc)
+{
+ int rc, i;
+ unsigned char *buffer;
+ size_t buflen;
+ unsigned char *value;
+ size_t valuelen;
+ int dummyrc;
+ int exmode;
+
+ if (!r_rc)
+ r_rc = &dummyrc;
+
+ *result = NULL;
+ *nbytes = 0;
+ *r_rc = 0;
+ for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
+ ;
+
+ if (app->appversion > 0x0100 && data_objects[i].get_immediate_in_v11)
+ {
+ exmode = 0;
+ rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
+ if (rc)
+ {
+ *r_rc = rc;
+ return NULL;
+ }
+ *result = buffer;
+ *nbytes = buflen;
+ return buffer;
+ }
+
+ value = NULL;
+ rc = -1;
+ if (data_objects[i].tag && data_objects[i].get_from)
+ {
+ rc = get_cached_data (app, data_objects[i].get_from,
+ &buffer, &buflen,
+ (data_objects[i].dont_cache
+ || data_objects[i].get_immediate_in_v11),
+ data_objects[i].try_extlen);
+ if (!rc)
+ {
+ const unsigned char *s;
+
+ s = find_tlv_unchecked (buffer, buflen, tag, &valuelen);
+ if (!s)
+ value = NULL; /* not found */
+ else if (valuelen > buflen - (s - buffer))
+ {
+ log_error ("warning: constructed DO too short\n");
+ value = NULL;
+ xfree (buffer); buffer = NULL;
+ }
+ else
+ value = buffer + (s - buffer);
+ }
+ }
+
+ if (!value) /* Not in a constructed DO, try simple. */
+ {
+ rc = get_cached_data (app, tag, &buffer, &buflen,
+ (data_objects[i].dont_cache
+ || data_objects[i].get_immediate_in_v11),
+ data_objects[i].try_extlen);
+ if (!rc)
+ {
+ value = buffer;
+ valuelen = buflen;
+ }
+ }
+
+ if (!rc)
+ {
+ *nbytes = valuelen;
+ *result = value;
+ return buffer;
+ }
+ *r_rc = rc;
+ return NULL;
+}
+
+
+static void
+dump_all_do (int slot)
+{
+ int rc, i, j;
+ unsigned char *buffer;
+ size_t buflen;
+
+ for (i=0; data_objects[i].tag; i++)
+ {
+ if (data_objects[i].get_from)
+ continue;
+
+ /* We don't try extended length APDU because such large DO would
+ be pretty useless in a log file. */
+ rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
+ if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
+ ;
+ else if (rc)
+ log_info ("DO '%s' not available: %s\n",
+ data_objects[i].desc, gpg_strerror (rc));
+ else
+ {
+ if (data_objects[i].binary)
+ {
+ log_info ("DO '%s': ", data_objects[i].desc);
+ log_printhex (buffer, buflen, "");
+ }
+ else
+ log_info ("DO '%s': '%.*s'\n",
+ data_objects[i].desc,
+ (int)buflen, buffer); /* FIXME: sanitize */
+
+ if (data_objects[i].constructed)
+ {
+ for (j=0; data_objects[j].tag; j++)
+ {
+ const unsigned char *value;
+ size_t valuelen;
+
+ if (j==i || data_objects[i].tag != data_objects[j].get_from)
+ continue;
+ value = find_tlv_unchecked (buffer, buflen,
+ data_objects[j].tag, &valuelen);
+ if (!value)
+ ; /* not found */
+ else if (valuelen > buflen - (value - buffer))
+ log_error ("warning: constructed DO too short\n");
+ else
+ {
+ if (data_objects[j].binary)
+ {
+ log_info ("DO '%s': ", data_objects[j].desc);
+ if (valuelen > 200)
+ log_info ("[%u]\n", (unsigned int)valuelen);
+ else
+ log_printhex (value, valuelen, "");
+ }
+ else
+ log_info ("DO '%s': '%.*s'\n",
+ data_objects[j].desc,
+ (int)valuelen, value); /* FIXME: sanitize */
+ }
+ }
+ }
+ }
+ xfree (buffer); buffer = NULL;
+ }
+}
+
+
+/* Count the number of bits, assuming the A represents an unsigned big
+ integer of length LEN bytes. */
+static unsigned int
+count_bits (const unsigned char *a, size_t len)
+{
+ unsigned int n = len * 8;
+ int i;
+
+ for (; len && !*a; len--, a++, n -=8)
+ ;
+ if (len)
+ {
+ for (i=7; i && !(*a & (1<<i)); i--)
+ n--;
+ }
+ return n;
+}
+
+/* GnuPG makes special use of the login-data DO, this function parses
+ the login data to store the flags for later use. It may be called
+ at any time and should be called after changing the login-data DO.
+
+ Everything up to a LF is considered a mailbox or account name. If
+ the first LF is followed by DC4 (0x14) control sequence are
+ expected up to the next LF. Control sequences are separated by FS
+ (0x18) and consist of key=value pairs. There are two keys defined:
+
+ F=<flags>
+
+ Where FLAGS is a plain hexadecimal number representing flag values.
+ The lsb is here the rightmost bit. Defined flags bits are:
+
+ Bit 0 = CHV1 and CHV2 are not synchronized
+ Bit 1 = CHV2 has been set to the default PIN of "123456"
+ (this implies that bit 0 is also set).
+
+ P=<pinpad-request>
+
+ Where PINPAD_REQUEST is in the format of: <n> or <n>,<m>.
+ N for user PIN, M for admin PIN. If M is missing it means M=N.
+ 0 means to force not to use pinpad.
+
+*/
+static void
+parse_login_data (app_t app)
+{
+ unsigned char *buffer, *p;
+ size_t buflen, len;
+ void *relptr;
+
+ /* Set defaults. */
+ app->app_local->flags.no_sync = 0;
+ app->app_local->flags.def_chv2 = 0;
+ app->app_local->pinpad.specified = 0;
+ app->app_local->pinpad.fixedlen_user = -1;
+ app->app_local->pinpad.fixedlen_admin = -1;
+
+ /* Read the DO. */
+ relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
+ if (!relptr)
+ return; /* Ooops. */
+ for (; buflen; buflen--, buffer++)
+ if (*buffer == '\n')
+ break;
+ if (buflen < 2 || buffer[1] != '\x14')
+ {
+ xfree (relptr);
+ return; /* No control sequences. */
+ }
+
+ buflen--;
+ buffer++;
+ do
+ {
+ buflen--;
+ buffer++;
+ if (buflen > 1 && *buffer == 'F' && buffer[1] == '=')
+ {
+ /* Flags control sequence found. */
+ int lastdig = 0;
+
+ /* For now we are only interested in the last digit, so skip
+ any leading digits but bail out on invalid characters. */
+ for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
+ lastdig = xtoi_1 (p);
+ buffer = p;
+ buflen = len;
+ if (len && !(*p == '\n' || *p == '\x18'))
+ goto next; /* Invalid characters in field. */
+ app->app_local->flags.no_sync = !!(lastdig & 1);
+ app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
+ }
+ else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=')
+ {
+ /* Pinpad request control sequence found. */
+ buffer += 2;
+ buflen -= 2;
+
+ if (buflen)
+ {
+ if (digitp (buffer))
+ {
+ char *q;
+ int n, m;
+
+ n = strtol (buffer, &q, 10);
+ if (q >= (char *)buffer + buflen
+ || *q == '\x18' || *q == '\n')
+ m = n;
+ else
+ {
+ if (*q++ != ',' || !digitp (q))
+ goto next;
+ m = strtol (q, &q, 10);
+ }
+
+ if (buflen < ((unsigned char *)q - buffer))
+ break;
+
+ buflen -= ((unsigned char *)q - buffer);
+ buffer = q;
+
+ if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
+ goto next;
+ app->app_local->pinpad.specified = 1;
+ app->app_local->pinpad.fixedlen_user = n;
+ app->app_local->pinpad.fixedlen_admin = m;
+ }
+ }
+ }
+ next:
+ /* Skip to FS (0x18) or LF (\n). */
+ for (; buflen && *buffer != '\x18' && *buffer != '\n'; buflen--)
+ buffer++;
+ }
+ while (buflen && *buffer != '\n');
+
+ xfree (relptr);
+}
+
+
+#define MAX_ARGS_STORE_FPR 3
+
+/* Note, that FPR must be at least 20 bytes. */
+static gpg_error_t
+store_fpr (app_t app, int keynumber, u32 timestamp, unsigned char *fpr,
+ int algo, ...)
+{
+ unsigned int n, nbits;
+ unsigned char *buffer, *p;
+ int tag, tag2;
+ int rc;
+ const unsigned char *m[MAX_ARGS_STORE_FPR];
+ size_t mlen[MAX_ARGS_STORE_FPR];
+ va_list ap;
+ int argc;
+ int i;
+
+ n = 6; /* key packet version, 4-byte timestamps, and algorithm */
+ if (algo == PUBKEY_ALGO_ECDH)
+ argc = 3;
+ else
+ argc = 2;
+
+ va_start (ap, algo);
+ for (i = 0; i < argc; i++)
+ {
+ m[i] = va_arg (ap, const unsigned char *);
+ mlen[i] = va_arg (ap, size_t);
+ if (algo == PUBKEY_ALGO_RSA || i == 1)
+ n += 2;
+ n += mlen[i];
+ }
+ va_end (ap);
+
+ p = buffer = xtrymalloc (3 + n);
+ if (!buffer)
+ return gpg_error_from_syserror ();
+
+ *p++ = 0x99; /* ctb */
+ *p++ = n >> 8; /* 2 byte length header */
+ *p++ = n;
+ *p++ = 4; /* key packet version */
+ *p++ = timestamp >> 24;
+ *p++ = timestamp >> 16;
+ *p++ = timestamp >> 8;
+ *p++ = timestamp;
+ *p++ = algo;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (algo == PUBKEY_ALGO_RSA || i == 1)
+ {
+ nbits = count_bits (m[i], mlen[i]);
+ *p++ = nbits >> 8;
+ *p++ = nbits;
+ }
+ memcpy (p, m[i], mlen[i]);
+ p += mlen[i];
+ }
+
+ gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
+
+ xfree (buffer);
+
+ tag = (app->appversion > 0x0007? 0xC7 : 0xC6) + keynumber;
+ flush_cache_item (app, 0xC5);
+ tag2 = 0xCE + keynumber;
+ flush_cache_item (app, 0xCD);
+
+ rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
+ if (rc)
+ log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
+
+ if (!rc && app->appversion > 0x0100)
+ {
+ unsigned char buf[4];
+
+ buf[0] = timestamp >> 24;
+ buf[1] = timestamp >> 16;
+ buf[2] = timestamp >> 8;
+ buf[3] = timestamp;
+
+ rc = iso7816_put_data (app->slot, 0, tag2, buf, 4);
+ if (rc)
+ log_error (_("failed to store the creation date: %s\n"),
+ gpg_strerror (rc));
+ }
+
+ return rc;
+}
+
+
+static void
+send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
+ int number, const unsigned char *fpr)
+{
+ int i;
+ char buf[41];
+ char numbuf[25];
+
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ if (i==20)
+ return; /* All zero. */
+ bin2hex (fpr, 20, buf);
+ if (number == -1)
+ *numbuf = 0; /* Don't print the key number */
+ else
+ sprintf (numbuf, "%d", number);
+ send_status_info (ctrl, keyword,
+ numbuf, (size_t)strlen(numbuf),
+ buf, (size_t)strlen (buf), NULL, 0);
+}
+
+static void
+send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
+ int number, const unsigned char *stamp)
+{
+ char numbuf1[50], numbuf2[50];
+ unsigned long value;
+
+ value = buf32_to_ulong (stamp);
+ if (!value)
+ return;
+ sprintf (numbuf1, "%d", number);
+ sprintf (numbuf2, "%lu", value);
+ send_status_info (ctrl, keyword,
+ numbuf1, (size_t)strlen(numbuf1),
+ numbuf2, (size_t)strlen(numbuf2), NULL, 0);
+}
+
+static void
+send_key_data (ctrl_t ctrl, const char *name,
+ const unsigned char *a, size_t alen)
+{
+ char *buffer, *buf;
+ size_t buflen;
+
+ buffer = buf = bin2hex (a, alen, NULL);
+ if (!buffer)
+ {
+ log_error ("memory allocation error in send_key_data\n");
+ return;
+ }
+ buflen = strlen (buffer);
+
+ /* 768 is the hexified size for the modulus of an 3072 bit key. We
+ use extra chunks to transmit larger data (i.e for 4096 bit). */
+ for ( ;buflen > 768; buflen -= 768, buf += 768)
+ send_status_info (ctrl, "KEY-DATA",
+ "-", 1,
+ buf, 768,
+ NULL, 0);
+ send_status_info (ctrl, "KEY-DATA",
+ name, (size_t)strlen(name),
+ buf, buflen,
+ NULL, 0);
+ xfree (buffer);
+}
+
+
+static void
+send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int keyno)
+{
+ char buffer[200];
+
+ assert (keyno >=0 && keyno < DIM(app->app_local->keyattr));
+
+ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+ snprintf (buffer, sizeof buffer, "%d 1 rsa%u %u %d",
+ keyno+1,
+ app->app_local->keyattr[keyno].rsa.n_bits,
+ app->app_local->keyattr[keyno].rsa.e_bits,
+ app->app_local->keyattr[keyno].rsa.format);
+ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+ {
+ snprintf (buffer, sizeof buffer, "%d %d %s",
+ keyno+1,
+ keyno==1? PUBKEY_ALGO_ECDH :
+ (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+ PUBKEY_ALGO_EDDSA : PUBKEY_ALGO_ECDSA,
+ app->app_local->keyattr[keyno].ecc.curve);
+ }
+ else
+ snprintf (buffer, sizeof buffer, "%d 0 0 UNKNOWN", keyno+1);
+
+ send_status_direct (ctrl, keyword, buffer);
+}
+
+
+#define RSA_SMALL_SIZE_KEY 1952
+#define RSA_SMALL_SIZE_OP 2048
+
+static int
+determine_rsa_response (app_t app, int keyno)
+{
+ int size;
+
+ size = 2 + 3 /* header */
+ + 4 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.n_bits+7)/8
+ + 2 /* tag+len */ + (app->app_local->keyattr[keyno].rsa.e_bits+7)/8;
+
+ return size;
+}
+
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ static struct {
+ const char *name;
+ int tag;
+ int special;
+ } table[] = {
+ { "DISP-NAME", 0x005B },
+ { "LOGIN-DATA", 0x005E },
+ { "DISP-LANG", 0x5F2D },
+ { "DISP-SEX", 0x5F35 },
+ { "PUBKEY-URL", 0x5F50 },
+ { "KEY-FPR", 0x00C5, 3 },
+ { "KEY-TIME", 0x00CD, 4 },
+ { "KEY-ATTR", 0x0000, -5 },
+ { "CA-FPR", 0x00C6, 3 },
+ { "CHV-STATUS", 0x00C4, 1 },
+ { "SIG-COUNTER", 0x0093, 2 },
+ { "SERIALNO", 0x004F, -1 },
+ { "AID", 0x004F },
+ { "EXTCAP", 0x0000, -2 },
+ { "PRIVATE-DO-1", 0x0101 },
+ { "PRIVATE-DO-2", 0x0102 },
+ { "PRIVATE-DO-3", 0x0103 },
+ { "PRIVATE-DO-4", 0x0104 },
+ { "$AUTHKEYID", 0x0000, -3 },
+ { "$ENCRKEYID", 0x0000, -6 },
+ { "$SIGNKEYID", 0x0000, -7 },
+ { "$DISPSERIALNO",0x0000, -4 },
+ { "KDF", 0x00F9, 5 },
+ { "MANUFACTURER", 0x0000, -8 },
+ { NULL, 0 }
+ };
+ int idx, i, rc;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+
+ if (table[idx].special == -1)
+ {
+ /* The serial number is very special. We could have used the
+ AID DO to retrieve it. The AID DO is available anyway but
+ not hex formatted. */
+ char *serial = app_get_serialno (app);
+
+ if (serial)
+ {
+ send_status_direct (ctrl, "SERIALNO", serial);
+ xfree (serial);
+ }
+ return 0;
+ }
+ if (table[idx].special == -2)
+ {
+ char tmp[110];
+
+ snprintf (tmp, sizeof tmp,
+ "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d "
+ "sm=%d si=%u dec=%d bt=%d kdf=%d",
+ app->app_local->extcap.get_challenge,
+ app->app_local->extcap.key_import,
+ app->app_local->extcap.change_force_chv,
+ app->app_local->extcap.private_dos,
+ app->app_local->extcap.max_certlen_3,
+ app->app_local->extcap.algo_attr_change,
+ (app->app_local->extcap.sm_supported
+ ? (app->app_local->extcap.sm_algo == 0? CIPHER_ALGO_3DES :
+ (app->app_local->extcap.sm_algo == 1?
+ CIPHER_ALGO_AES : CIPHER_ALGO_AES256))
+ : 0),
+ app->app_local->status_indicator,
+ app->app_local->extcap.has_decrypt,
+ app->app_local->extcap.has_button,
+ app->app_local->extcap.kdf_do);
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
+ if (table[idx].special == -3)
+ {
+ char const tmp[] = "OPENPGP.3";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
+ if (table[idx].special == -4)
+ {
+ char *serial = app_get_dispserialno (app, 0);
+
+ if (serial)
+ {
+ send_status_info (ctrl, table[idx].name,
+ serial, strlen (serial), NULL, 0);
+ xfree (serial);
+ return 0;
+ }
+ return gpg_error (GPG_ERR_INV_NAME);
+ }
+ if (table[idx].special == -5)
+ {
+ for (i=0; i < 3; i++)
+ send_key_attr (ctrl, app, table[idx].name, i);
+ return 0;
+ }
+ if (table[idx].special == -6)
+ {
+ char const tmp[] = "OPENPGP.2";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
+ if (table[idx].special == -7)
+ {
+ char const tmp[] = "OPENPGP.1";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
+ if (table[idx].special == -8)
+ {
+ return send_status_printf
+ (ctrl, table[idx].name, "%u %s",
+ app->app_local->manufacturer,
+ get_manufacturer (app->app_local->manufacturer));
+ }
+
+ relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
+ if (relptr)
+ {
+ if (table[idx].special == 1)
+ {
+ char numbuf[7*23];
+
+ for (i=0,*numbuf=0; i < valuelen && i < 7; i++)
+ sprintf (numbuf+strlen (numbuf), " %d", value[i]);
+ send_status_info (ctrl, table[idx].name,
+ numbuf, strlen (numbuf), NULL, 0);
+ }
+ else if (table[idx].special == 2)
+ {
+ char numbuf[50];
+
+ sprintf (numbuf, "%lu", convert_sig_counter_value (value, valuelen));
+ send_status_info (ctrl, table[idx].name,
+ numbuf, strlen (numbuf), NULL, 0);
+ }
+ else if (table[idx].special == 3)
+ {
+ if (valuelen >= 60)
+ for (i=0; i < 3; i++)
+ send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20);
+ }
+ else if (table[idx].special == 4)
+ {
+ if (valuelen >= 12)
+ for (i=0; i < 3; i++)
+ send_fprtime_if_not_null (ctrl, table[idx].name, i+1, value+i*4);
+ }
+ else if (table[idx].special == 5)
+ {
+ if ((valuelen == KDF_DATA_LENGTH_MIN
+ || valuelen == KDF_DATA_LENGTH_MAX)
+ && (value[2] == 0x03))
+ app->app_local->pinpad.disabled = 1;
+ else
+ app->app_local->pinpad.disabled = 0;
+
+ send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
+ }
+ else
+ send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
+
+ xfree (relptr);
+ }
+ else
+ {
+ if (table[idx].special == 5)
+ app->app_local->pinpad.disabled = 0;
+ }
+ return rc;
+}
+
+
+/* Return the DISP-NAME without any padding characters. Caller must
+ * free the result. If not found or empty NULL is returned. */
+static char *
+get_disp_name (app_t app)
+{
+ int rc;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ char *string;
+ char *p, *given;
+ char *result;
+
+ relptr = get_one_do (app, 0x005B, &value, &valuelen, &rc);
+ if (!relptr)
+ return NULL;
+
+ string = xtrymalloc (valuelen + 1);
+ if (!string)
+ {
+ xfree (relptr);
+ return NULL;
+ }
+ memcpy (string, value, valuelen);
+ string[valuelen] = 0;
+ xfree (relptr);
+
+ /* Swap surname and given name. */
+ given = strstr (string, "<<");
+ for (p = string; *p; p++)
+ if (*p == '<')
+ *p = ' ';
+
+ if (given && given[2])
+ {
+ *given = 0;
+ given += 2;
+ result = strconcat (given, " ", string, NULL);
+ }
+ else
+ {
+ result = string;
+ string = NULL;
+ }
+
+ xfree (string);
+ return result;
+}
+
+
+/* Return the pretty formatted serialnumber. On error NULL is
+ * returned. */
+static char *
+get_disp_serialno (app_t app)
+{
+ char *serial = app_get_serialno (app);
+
+ /* For our OpenPGP cards we do not want to show the entire serial
+ * number but a nicely reformatted actual serial number. */
+ if (serial && strlen (serial) > 16+12)
+ {
+ memmove (serial, serial+16, 4);
+ serial[4] = ' ';
+ /* memmove (serial+5, serial+20, 4); */
+ /* serial[9] = ' '; */
+ /* memmove (serial+10, serial+24, 4); */
+ /* serial[14] = 0; */
+ memmove (serial+5, serial+20, 8);
+ serial[13] = 0;
+ }
+ return serial;
+}
+
+
+/* Return the number of remaining tries for the standard or the admin
+ * pw. Returns -1 on card error. */
+static int
+get_remaining_tries (app_t app, int adminpw)
+{
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int remaining;
+
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ return -1;
+ }
+ remaining = value[adminpw? 6 : 4];
+ xfree (relptr);
+ return remaining;
+}
+
+
+/* Retrieve the fingerprint from the card inserted in SLOT and write
+ the according hex representation to FPR. Caller must have provide
+ a buffer at FPR of least 41 bytes. Returns 0 on success or an
+ error code. */
+static gpg_error_t
+retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
+{
+ gpg_error_t err = 0;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+
+ assert (keyno >=0 && keyno <= 2);
+
+ relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
+ if (relptr && valuelen >= 60)
+ bin2hex (value+keyno*20, 20, fpr);
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ xfree (relptr);
+ return err;
+}
+
+
+/* Retrieve the public key material for the RSA key, whose fingerprint
+ is FPR, from gpg output, which can be read through the stream FP.
+ The RSA modulus will be stored at the address of M and MLEN, the
+ public exponent at E and ELEN. Returns zero on success, an error
+ code on failure. Caller must release the allocated buffers at M
+ and E if the function returns success. */
+static gpg_error_t
+retrieve_key_material (FILE *fp, const char *hexkeyid,
+ const unsigned char **m, size_t *mlen,
+ const unsigned char **e, size_t *elen)
+{
+ gcry_error_t err = 0;
+ char *line = NULL; /* read_line() buffer. */
+ size_t line_size = 0; /* Helper for for read_line. */
+ int found_key = 0; /* Helper to find a matching key. */
+ unsigned char *m_new = NULL;
+ unsigned char *e_new = NULL;
+ size_t m_new_n = 0;
+ size_t e_new_n = 0;
+
+ /* Loop over all records until we have found the subkey
+ corresponding to the fingerprint. Inm general the first record
+ should be the pub record, but we don't rely on that. Given that
+ we only need to look at one key, it is sufficient to compare the
+ keyid so that we don't need to look at "fpr" records. */
+ for (;;)
+ {
+ char *p;
+ char *fields[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ int nfields;
+ size_t max_length;
+ gcry_mpi_t mpi;
+ int i;
+
+ max_length = 4096;
+ i = read_line (fp, &line, &line_size, &max_length);
+ if (!i)
+ break; /* EOF. */
+ if (i < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave; /* Error. */
+ }
+ if (!max_length)
+ {
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave; /* Line truncated - we better stop processing. */
+ }
+
+ /* Parse the line into fields. */
+ for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
+ {
+ fields[nfields] = p;
+ p = strchr (p, ':');
+ if (p)
+ *(p++) = 0;
+ }
+ if (!nfields)
+ continue; /* No fields at all - skip line. */
+
+ if (!found_key)
+ {
+ if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ && nfields > 4 && !strcmp (fields[4], hexkeyid))
+ found_key = 1;
+ continue;
+ }
+
+ if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ break; /* Next key - stop. */
+
+ if ( strcmp (fields[0], "pkd") )
+ continue; /* Not a key data record. */
+ if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
+ || (!i && m_new) || (i && e_new))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave; /* Error: Invalid key data record or not an RSA key. */
+ }
+
+ err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
+ if (err)
+ mpi = NULL;
+ else if (!i)
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi);
+ else
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi);
+ gcry_mpi_release (mpi);
+ if (err)
+ goto leave;
+ }
+
+ if (m_new && e_new)
+ {
+ *m = m_new;
+ *mlen = m_new_n;
+ m_new = NULL;
+ *e = e_new;
+ *elen = e_new_n;
+ e_new = NULL;
+ }
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+
+ leave:
+ xfree (m_new);
+ xfree (e_new);
+ xfree (line);
+ return err;
+}
+
+
+static gpg_error_t
+rsa_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+{
+ gpg_error_t err;
+ const unsigned char *m, *e;
+ size_t mlen, elen;
+ unsigned char *mbuf = NULL, *ebuf = NULL;
+
+ m = find_tlv (data, datalen, 0x0081, &mlen);
+ if (!m)
+ {
+ log_error (_("response does not contain the RSA modulus\n"));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ e = find_tlv (data, datalen, 0x0082, &elen);
+ if (!e)
+ {
+ log_error (_("response does not contain the RSA public exponent\n"));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ if (ctrl)
+ {
+ send_key_data (ctrl, "n", m, mlen);
+ send_key_data (ctrl, "e", e, elen);
+ }
+
+ for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+ ;
+ for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+ ;
+
+ if (ctrl)
+ {
+ unsigned char fprbuf[20];
+
+ err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+ m, mlen, e, elen);
+ if (err)
+ return err;
+
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+ }
+
+ mbuf = xtrymalloc (mlen + 1);
+ if (!mbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ /* Prepend numbers with a 0 if needed. */
+ if (mlen && (*m & 0x80))
+ {
+ *mbuf = 0;
+ memcpy (mbuf+1, m, mlen);
+ mlen++;
+ }
+ else
+ memcpy (mbuf, m, mlen);
+
+ ebuf = xtrymalloc (elen + 1);
+ if (!ebuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ /* Prepend numbers with a 0 if needed. */
+ if (elen && (*e & 0x80))
+ {
+ *ebuf = 0;
+ memcpy (ebuf+1, e, elen);
+ elen++;
+ }
+ else
+ memcpy (ebuf, e, elen);
+
+ err = gcry_sexp_build (r_sexp, NULL, "(public-key(rsa(n%b)(e%b)))",
+ (int)mlen, mbuf, (int)elen, ebuf);
+ leave:
+ xfree (mbuf);
+ xfree (ebuf);
+ return err;
+}
+
+
+/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE. */
+static const unsigned char*
+ecdh_params (const char *curve)
+{
+ unsigned int nbits;
+
+ openpgp_curve_to_oid (curve, &nbits, NULL);
+
+ /* See RFC-6637 for those constants.
+ 0x03: Number of bytes
+ 0x01: Version for this parameter format
+ KDF hash algo
+ KEK symmetric cipher algo
+ */
+ if (nbits <= 256)
+ return (const unsigned char*)"\x03\x01\x08\x07";
+ else if (nbits <= 384)
+ return (const unsigned char*)"\x03\x01\x09\x08";
+ else
+ return (const unsigned char*)"\x03\x01\x0a\x09";
+}
+
+static gpg_error_t
+ecc_read_pubkey (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ const unsigned char *data, size_t datalen, gcry_sexp_t *r_sexp)
+{
+ gpg_error_t err;
+ unsigned char *qbuf = NULL;
+ const unsigned char *ecc_q;
+ size_t ecc_q_len;
+ gcry_mpi_t oid = NULL;
+ int n;
+ const char *curve;
+ const char *oidstr;
+ const unsigned char *oidbuf;
+ size_t oid_len;
+ int algo;
+ const char *format;
+
+ ecc_q = find_tlv (data, datalen, 0x0086, &ecc_q_len);
+ if (!ecc_q)
+ {
+ log_error (_("response does not contain the EC public key\n"));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ curve = app->app_local->keyattr[keyno].ecc.curve;
+ oidstr = openpgp_curve_to_oid (curve, NULL, NULL);
+ err = openpgp_oid_from_str (oidstr, &oid);
+ if (err)
+ return err;
+ oidbuf = gcry_mpi_get_opaque (oid, &n);
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ oid_len = (n+7)/8;
+
+ qbuf = xtrymalloc (ecc_q_len + 1);
+ if (!qbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+ { /* Prepend 0x40 prefix. */
+ *qbuf = 0x40;
+ memcpy (qbuf+1, ecc_q, ecc_q_len);
+ ecc_q_len++;
+ }
+ else
+ memcpy (qbuf, ecc_q, ecc_q_len);
+
+ if (ctrl)
+ {
+ send_key_data (ctrl, "q", qbuf, ecc_q_len);
+ send_key_data (ctrl, "curve", oidbuf, oid_len);
+ }
+
+ if (keyno == 1)
+ {
+ if (ctrl)
+ send_key_data (ctrl, "kdf/kek", ecdh_params (curve), (size_t)4);
+ algo = PUBKEY_ALGO_ECDH;
+ }
+ else
+ {
+ if ((app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+ algo = PUBKEY_ALGO_EDDSA;
+ else
+ algo = PUBKEY_ALGO_ECDSA;
+ }
+
+ if (ctrl)
+ {
+ unsigned char fprbuf[20];
+
+ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+ qbuf, ecc_q_len, ecdh_params (curve), (size_t)4);
+ if (err)
+ goto leave;
+
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+ }
+
+ if (!(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK))
+ format = "(public-key(ecc(curve%s)(q%b)))";
+ else if (keyno == 1)
+ format = "(public-key(ecc(curve%s)(flags djb-tweak)(q%b)))";
+ else
+ format = "(public-key(ecc(curve%s)(flags eddsa)(q%b)))";
+
+ err = gcry_sexp_build (r_sexp, NULL, format,
+ app->app_local->keyattr[keyno].ecc.curve,
+ (int)ecc_q_len, qbuf);
+ leave:
+ gcry_mpi_release (oid);
+ xfree (qbuf);
+ return err;
+}
+
+
+/* Compute the keygrip form the local info and store it there. */
+static gpg_error_t
+store_keygrip (app_t app, int keyno)
+{
+ gpg_error_t err;
+ unsigned char grip[20];
+
+ err = keygrip_from_canon_sexp (app->app_local->pk[keyno].key,
+ app->app_local->pk[keyno].keylen,
+ grip);
+ if (err)
+ return err;
+
+ bin2hex (grip, 20, app->app_local->pk[keyno].keygrip_str);
+ return 0;
+}
+
+
+/* Parse tag-length-value data for public key in BUFFER of BUFLEN
+ length. Key of KEYNO in APP is updated with an S-expression of
+ public key. When CTRL is not NULL, fingerprint is computed with
+ CREATED_AT, and fingerprint is written to the card, and key data
+ and fingerprint are send back to the client side.
+ */
+static gpg_error_t
+read_public_key (app_t app, ctrl_t ctrl, u32 created_at, int keyno,
+ const unsigned char *buffer, size_t buflen)
+{
+ gpg_error_t err;
+ const unsigned char *data;
+ size_t datalen;
+ gcry_sexp_t s_pkey = NULL;
+
+ data = find_tlv (buffer, buflen, 0x7F49, &datalen);
+ if (!data)
+ {
+ log_error (_("response does not contain the public key data\n"));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+ err = rsa_read_pubkey (app, ctrl, created_at, keyno,
+ data, datalen, &s_pkey);
+ else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
+ err = ecc_read_pubkey (app, ctrl, created_at, keyno,
+ data, datalen, &s_pkey);
+ else
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ if (!err)
+ {
+ unsigned char *keybuf;
+ size_t len;
+
+ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+ keybuf = xtrymalloc (len);
+ if (!data)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_sexp_release (s_pkey);
+ return err;
+ }
+
+ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+ gcry_sexp_release (s_pkey);
+
+ app->app_local->pk[keyno].key = keybuf;
+ /* Decrement for trailing '\0' */
+ app->app_local->pk[keyno].keylen = len - 1;
+
+ err = store_keygrip (app, keyno);
+ }
+
+ return err;
+}
+
+
+/* Get the public key for KEYNO and store it as an S-expression with
+ the APP handle. On error that field gets cleared. If we already
+ know about the public key we will just return. Note that this does
+ not mean a key is available; this is solely indicated by the
+ presence of the app->app_local->pk[KEYNO].key field.
+
+ Note that GnuPG 1.x does not need this and it would be too time
+ consuming to send it just for the fun of it. However, given that we
+ use the same code in gpg 1.4, we can't use the gcry S-expression
+ here but need to open encode it. */
+static gpg_error_t
+get_public_key (app_t app, int keyno)
+{
+ gpg_error_t err = 0;
+ unsigned char *buffer;
+ const unsigned char *m, *e;
+ size_t buflen;
+ size_t mlen = 0;
+ size_t elen = 0;
+ char *keybuf = NULL;
+ gcry_sexp_t s_pkey;
+ size_t len;
+
+ if (keyno < 0 || keyno > 2)
+ return gpg_error (GPG_ERR_INV_ID);
+
+ /* Already cached? */
+ if (app->app_local->pk[keyno].read_done)
+ return 0;
+
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+
+ m = e = NULL; /* (avoid cc warning) */
+
+ if (app->appversion > 0x0100)
+ {
+ int exmode, le_value;
+
+ /* We may simply read the public key out of these cards. */
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[keyno].rsa.n_bits > RSA_SMALL_SIZE_KEY)
+ {
+ exmode = 1; /* Use extended length. */
+ le_value = determine_rsa_response (app, keyno);
+ }
+ else
+ {
+ exmode = 0;
+ le_value = 256; /* Use legacy value. */
+ }
+
+ err = iso7816_read_public_key (app->slot, exmode,
+ (keyno == 0? "\xB6" :
+ keyno == 1? "\xB8" : "\xA4"),
+ 2, le_value, &buffer, &buflen);
+ if (err)
+ {
+ /* Yubikey returns wrong code. Fix it up. */
+ /*
+ * NOTE: It's not correct to blindly change the error code,
+ * however, for our experiences, it is only Yubikey...
+ */
+ err = gpg_error (GPG_ERR_NO_OBJ);
+ log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = read_public_key (app, NULL, 0U, keyno, buffer, buflen);
+ }
+ else
+ {
+ /* Due to a design problem in v1.0 cards we can't get the public
+ key out of these cards without doing a verify on CHV3.
+ Clearly that is not an option and thus we try to locate the
+ key using an external helper.
+
+ The helper we use here is gpg itself, which should know about
+ the key in any case. */
+
+ char fpr[41];
+ char *hexkeyid;
+ char *command = NULL;
+ FILE *fp;
+ int ret;
+
+ buffer = NULL; /* We don't need buffer. */
+
+ err = retrieve_fpr_from_card (app, keyno, fpr);
+ if (err)
+ {
+ log_error ("error while retrieving fpr from card: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ hexkeyid = fpr + 24;
+
+ ret = gpgrt_asprintf
+ (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr);
+ if (ret < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ fp = popen (command, "r");
+ xfree (command);
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("running gpg failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
+ pclose (fp);
+ if (err)
+ {
+ log_error ("error while retrieving key material through pipe: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
+ (int)mlen, m, (int)elen, e);
+ if (err)
+ goto leave;
+
+ len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+
+ keybuf = xtrymalloc (len);
+ if (!keybuf)
+ {
+ err = gpg_error_from_syserror ();
+ gcry_sexp_release (s_pkey);
+ goto leave;
+ }
+
+ gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
+ gcry_sexp_release (s_pkey);
+
+ app->app_local->pk[keyno].key = (unsigned char*)keybuf;
+ /* Decrement for trailing '\0' */
+ app->app_local->pk[keyno].keylen = len - 1;
+
+ err = store_keygrip (app, keyno);
+ }
+
+ leave:
+ /* Set a flag to indicate that we tried to read the key. */
+ if (!err)
+ app->app_local->pk[keyno].read_done = 1;
+
+ xfree (buffer);
+ return err;
+}
+
+
+/* Send the KEYPAIRINFO back. KEY needs to be in the range [1,3].
+ This is used by the LEARN command. */
+static gpg_error_t
+send_keypair_info (app_t app, ctrl_t ctrl, int key)
+{
+ int keyno = key - 1;
+ gpg_error_t err = 0;
+ char idbuf[50];
+ const char *usage;
+
+ err = get_public_key (app, keyno);
+ if (err)
+ goto leave;
+
+ assert (keyno >= 0 && keyno <= 2);
+ if (!app->app_local->pk[keyno].key)
+ goto leave; /* No such key - ignore. */
+
+ switch (keyno)
+ {
+ case 0: usage = "sc"; break;
+ case 1: usage = "e"; break;
+ case 2: usage = "sa"; break;
+ default: usage = ""; break;
+ }
+
+ sprintf (idbuf, "OPENPGP.%d", keyno+1);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ app->app_local->pk[keyno].keygrip_str, 40,
+ idbuf, strlen (idbuf),
+ usage, strlen (usage),
+ NULL, (size_t)0);
+
+ leave:
+ return err;
+}
+
+
+/* Handle the LEARN command for OpenPGP. */
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err = 0;
+
+ (void)flags;
+
+ err = do_getattr (app, ctrl, "EXTCAP");
+ if (!err)
+ err = do_getattr (app, ctrl, "MANUFACTURER");
+ if (!err)
+ err = do_getattr (app, ctrl, "DISP-NAME");
+ if (!err)
+ err = do_getattr (app, ctrl, "DISP-LANG");
+ if (!err)
+ err = do_getattr (app, ctrl, "DISP-SEX");
+ if (!err)
+ err = do_getattr (app, ctrl, "PUBKEY-URL");
+ if (!err)
+ err = do_getattr (app, ctrl, "LOGIN-DATA");
+ if (!err)
+ err = do_getattr (app, ctrl, "KEY-FPR");
+ if (!err && app->appversion > 0x0100)
+ err = do_getattr (app, ctrl, "KEY-TIME");
+ if (!err)
+ err = do_getattr (app, ctrl, "CA-FPR");
+ if (!err)
+ err = do_getattr (app, ctrl, "CHV-STATUS");
+ if (!err)
+ err = do_getattr (app, ctrl, "SIG-COUNTER");
+ if (!err && app->app_local->extcap.kdf_do)
+ {
+ err = do_getattr (app, ctrl, "KDF");
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ }
+ if (!err && app->app_local->extcap.private_dos)
+ {
+ if (!err)
+ err = do_getattr (app, ctrl, "PRIVATE-DO-1");
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ if (!err)
+ err = do_getattr (app, ctrl, "PRIVATE-DO-2");
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ if (!err && app->did_chv2)
+ err = do_getattr (app, ctrl, "PRIVATE-DO-3");
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ if (!err && app->did_chv3)
+ err = do_getattr (app, ctrl, "PRIVATE-DO-4");
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ }
+ if (!err)
+ err = send_keypair_info (app, ctrl, 1);
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ if (!err)
+ err = send_keypair_info (app, ctrl, 2);
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ if (!err)
+ err = send_keypair_info (app, ctrl, 3);
+ if (gpg_err_code (err) == GPG_ERR_NO_OBJ)
+ err = 0;
+ /* Note: We do not send the Cardholder Certificate, because that is
+ relatively long and for OpenPGP applications not really needed. */
+ return err;
+}
+
+
+/* Handle the READKEY command for OpenPGP. On success a canonical
+ encoded S-expression with the public key will get stored at PK and
+ its length (for assertions) at PKLEN; the caller must release that
+ buffer. On error PK and PKLEN are not changed and an error code is
+ returned. */
+static gpg_error_t
+do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
+ unsigned char **pk, size_t *pklen)
+{
+ gpg_error_t err;
+ int keyno;
+ unsigned char *buf;
+
+ (void)ctrl;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 0;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 2;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = get_public_key (app, keyno);
+ if (err)
+ return err;
+
+ buf = app->app_local->pk[keyno].key;
+ if (!buf)
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+
+ if ((flags & APP_READKEY_FLAG_ADVANCED))
+ {
+ gcry_sexp_t s_key;
+
+ err = gcry_sexp_new (&s_key, buf, app->app_local->pk[keyno].keylen, 0);
+ if (err)
+ return err;
+
+ *pklen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+ *pk = xtrymalloc (*pklen);
+ if (!*pk)
+ {
+ err = gpg_error_from_syserror ();
+ *pklen = 0;
+ return err;
+ }
+
+ gcry_sexp_sprint (s_key, GCRYSEXP_FMT_ADVANCED, *pk, *pklen);
+ gcry_sexp_release (s_key);
+ /* Decrement for trailing '\0' */
+ *pklen = *pklen - 1;
+ }
+ else
+ {
+ *pklen = app->app_local->pk[keyno].keylen;
+ *pk = xtrymalloc (*pklen);
+ if (!*pk)
+ {
+ err = gpg_error_from_syserror ();
+ *pklen = 0;
+ return err;
+ }
+ memcpy (*pk, buf, *pklen);
+ }
+
+ return 0;
+}
+
+/* Read the standard certificate of an OpenPGP v2 card. It is
+ returned in a freshly allocated buffer with that address stored at
+ CERT and the length of the certificate stored at CERTLEN. CERTID
+ needs to be set to "OPENPGP.3". */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ void *relptr;
+
+ *cert = NULL;
+ *certlen = 0;
+ if (strcmp (certid, "OPENPGP.3"))
+ return gpg_error (GPG_ERR_INV_ID);
+ if (!app->app_local->extcap.is_v2)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
+ if (!relptr)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ if (!buflen)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ else if (!(*cert = xtrymalloc (buflen)))
+ err = gpg_error_from_syserror ();
+ else
+ {
+ memcpy (*cert, buffer, buflen);
+ *certlen = buflen;
+ err = 0;
+ }
+ xfree (relptr);
+ return err;
+}
+
+
+/* Decide if we use the pinpad of the reader for PIN input according
+ to the user preference on the card, and the capability of the
+ reader. This routine is only called when the reader has pinpad.
+ Returns 0 if we use pinpad, 1 otherwise. */
+static int
+check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
+{
+ if (app->app_local->pinpad.disabled)
+ return 1;
+
+ if (app->app_local->pinpad.specified == 0) /* No preference on card. */
+ {
+ if (pininfo->fixedlen == 0) /* Reader has varlen capability. */
+ return 0; /* Then, use pinpad. */
+ else
+ /*
+ * Reader has limited capability, and it may not match PIN of
+ * the card.
+ */
+ return 1;
+ }
+
+ if (admin_pin)
+ pininfo->fixedlen = app->app_local->pinpad.fixedlen_admin;
+ else
+ pininfo->fixedlen = app->app_local->pinpad.fixedlen_user;
+
+ if (pininfo->fixedlen == 0 /* User requests disable pinpad. */
+ || pininfo->fixedlen < pininfo->minlen
+ || pininfo->fixedlen > pininfo->maxlen
+ /* Reader doesn't have the capability to input a PIN which
+ * length is FIXEDLEN. */)
+ return 1;
+
+ return 0;
+}
+
+
+/* Return a string with information about the card for use in a
+ * prompt. Returns NULL on memory failure. */
+static char *
+get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
+{
+ char *serial, *disp_name, *rembuf, *tmpbuf, *result;
+
+ serial = get_disp_serialno (app);
+ if (!serial)
+ return NULL;
+
+ disp_name = get_disp_name (app);
+ if (chvno == 1)
+ {
+ /* TRANSLATORS: Put a \x1f right before a colon. This can be
+ * used by pinentry to nicely align the names and values. Keep
+ * the %s at the start and end of the string. */
+ result = xtryasprintf (_("%s"
+ "Number\x1f: %s%%0A"
+ "Holder\x1f: %s%%0A"
+ "Counter\x1f: %lu"
+ "%s"),
+ "\x1e",
+ serial,
+ disp_name? disp_name:"",
+ sigcount,
+ "");
+ }
+ else
+ {
+ result = xtryasprintf (_("%s"
+ "Number\x1f: %s%%0A"
+ "Holder\x1f: %s"
+ "%s"),
+ "\x1e",
+ serial,
+ disp_name? disp_name:"",
+ "");
+ }
+ xfree (disp_name);
+ xfree (serial);
+
+ if (remaining != -1)
+ {
+ /* TRANSLATORS: This is the number of remaining attempts to
+ * enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
+ rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
+ if (!rembuf)
+ {
+ xfree (result);
+ return NULL;
+ }
+ tmpbuf = strconcat (result, "%0A%0A", rembuf, NULL);
+ xfree (rembuf);
+ if (!tmpbuf)
+ {
+ xfree (result);
+ return NULL;
+ }
+ xfree (result);
+ result = tmpbuf;
+ }
+
+ return result;
+}
+
+/* Compute hash if KDF-DO is available. CHVNO must be 0 for reset
+ code, 1 or 2 for user pin and 3 for admin pin.
+ */
+static gpg_error_t
+pin2hash_if_kdf (app_t app, int chvno, char *pinvalue, int *r_pinlen)
+{
+ gpg_error_t err = 0;
+ void *relptr = NULL;
+ unsigned char *buffer;
+ size_t buflen;
+
+ if (app->app_local->extcap.kdf_do
+ && (relptr = get_one_do (app, 0x00F9, &buffer, &buflen, NULL))
+ && buflen >= KDF_DATA_LENGTH_MIN && (buffer[2] == 0x03))
+ {
+ const char *salt;
+ unsigned long s2k_count;
+ char dek[32];
+ int salt_index;
+
+ s2k_count = (((unsigned int)buffer[8] << 24)
+ | (buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
+
+ if (buflen == KDF_DATA_LENGTH_MIN)
+ salt_index =14;
+ else if (buflen == KDF_DATA_LENGTH_MAX)
+ salt_index = (chvno==3 ? 34 : (chvno==0 ? 24 : 14));
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ salt = &buffer[salt_index];
+ err = gcry_kdf_derive (pinvalue, strlen (pinvalue),
+ GCRY_KDF_ITERSALTED_S2K,
+ DIGEST_ALGO_SHA256, salt, 8,
+ s2k_count, sizeof (dek), dek);
+ if (!err)
+ {
+ /* pinvalue has a buffer of MAXLEN_PIN+1, 32 is OK. */
+ *r_pinlen = 32;
+ memcpy (pinvalue, dek, *r_pinlen);
+ wipememory (dek, *r_pinlen);
+ }
+ }
+ else
+ *r_pinlen = strlen (pinvalue);
+
+ leave:
+ xfree (relptr);
+ return err;
+}
+
+
+/* Verify a CHV either using the pinentry or if possible by
+ using a pinpad. PINCB and PINCB_ARG describe the usual callback
+ for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
+ used with CHV1. PINVALUE is the address of a pointer which will
+ receive a newly allocated block with the actual PIN (this is useful
+ in case that PIN shall be used for another verify operation). The
+ caller needs to free this value. If the function returns with
+ success and NULL is stored at PINVALUE, the caller should take this
+ as an indication that the pinpad has been used.
+ */
+static gpg_error_t
+verify_a_chv (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg, int chvno, unsigned long sigcount,
+ char **pinvalue, int *pinlen)
+{
+ int rc = 0;
+ char *prompt_buffer = NULL;
+ const char *prompt;
+ pininfo_t pininfo;
+ int minlen = 6;
+ int remaining;
+
+ log_assert (chvno == 1 || chvno == 2);
+
+ *pinvalue = NULL;
+ *pinlen = 0;
+
+ remaining = get_remaining_tries (app, 0);
+ if (remaining == -1)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (chvno == 2 && app->app_local->flags.def_chv2)
+ {
+ /* Special case for def_chv2 mechanism. */
+ if (opt.verbose)
+ log_info (_("using default PIN as %s\n"), "CHV2");
+ rc = iso7816_verify (app->slot, 0x82, "123456", 6);
+ if (rc)
+ {
+ /* Verification of CHV2 with the default PIN failed,
+ although the card pretends to have the default PIN set as
+ CHV2. We better disable the def_chv2 flag now. */
+ log_info (_("failed to use default PIN as %s: %s"
+ " - disabling further default use\n"),
+ "CHV2", gpg_strerror (rc));
+ app->app_local->flags.def_chv2 = 0;
+ }
+ return rc;
+ }
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = minlen;
+
+ {
+ const char *firstline = _("||Please unlock the card");
+ char *infoblock = get_prompt_info (app, chvno, sigcount,
+ remaining < 3? remaining : -1);
+
+ prompt_buffer = strconcat (firstline, "%0A%0A", infoblock, NULL);
+ if (prompt_buffer)
+ prompt = prompt_buffer;
+ else
+ prompt = firstline; /* ENOMEM fallback. */
+
+ xfree (infoblock);
+ }
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
+ && !check_pinpad_request (app, &pininfo, 0))
+ {
+ /* The reader supports the verify command through the pinpad.
+ Note that the pincb appends a text to the prompt telling the
+ user to use the pinpad. */
+ rc = pincb (pincb_arg, prompt, NULL);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+ rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo);
+ /* Dismiss the prompt. */
+ pincb (pincb_arg, NULL, NULL);
+
+ log_assert (!*pinvalue);
+ }
+ else
+ {
+ /* The reader has no pinpad or we don't want to use it. */
+ rc = pincb (pincb_arg, prompt, pinvalue);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+
+ if (strlen (*pinvalue) < minlen)
+ {
+ log_error (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), chvno, minlen);
+ xfree (*pinvalue);
+ *pinvalue = NULL;
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = pin2hash_if_kdf (app, chvno, *pinvalue, pinlen);
+ if (!rc)
+ rc = iso7816_verify (app->slot, 0x80+chvno, *pinvalue, *pinlen);
+ }
+
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc));
+ xfree (*pinvalue);
+ *pinvalue = NULL;
+ flush_cache_after_error (app);
+ }
+
+ return rc;
+}
+
+
+/* Verify CHV2 if required. Depending on the configuration of the
+ card CHV1 will also be verified. */
+static gpg_error_t
+verify_chv2 (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+ char *pinvalue;
+ int pinlen;
+ int i;
+
+ if (app->did_chv2)
+ return 0; /* We already verified CHV2. */
+
+ /* Make sure we have load the public keys. */
+ for (i = 0; i < 3; i++)
+ get_public_key (app, i);
+
+ if (app->app_local->pk[1].key || app->app_local->pk[2].key)
+ {
+ rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue, &pinlen);
+ if (rc)
+ return rc;
+ app->did_chv2 = 1;
+
+ if (!app->did_chv1 && !app->force_chv1 && pinvalue && !opt.pcsc_shared)
+ {
+ /* For convenience we verify CHV1 here too. We do this only if
+ the card is not configured to require a verification before
+ each CHV1 controlled operation (force_chv1) and if we are not
+ using the pinpad (PINVALUE == NULL). */
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, pinlen);
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
+ flush_cache_after_error (app);
+ }
+ else
+ app->did_chv1 = 1;
+ }
+ }
+ else
+ {
+ rc = verify_a_chv (app, pincb, pincb_arg, 1, 0, &pinvalue, &pinlen);
+ if (rc)
+ return rc;
+ }
+
+ xfree (pinvalue);
+
+ return rc;
+}
+
+
+/* Build the prompt to enter the Admin PIN. The prompt depends on the
+ current sdtate of the card. */
+static gpg_error_t
+build_enter_admin_pin_prompt (app_t app, char **r_prompt)
+{
+ int remaining;
+ char *prompt;
+ char *infoblock;
+
+ *r_prompt = NULL;
+
+ remaining = get_remaining_tries (app, 1);
+ if (remaining == -1)
+ return gpg_error (GPG_ERR_CARD);
+ if (!remaining)
+ {
+ log_info (_("card is permanently locked!\n"));
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ log_info (ngettext("%d Admin PIN attempt remaining before card"
+ " is permanently locked\n",
+ "%d Admin PIN attempts remaining before card"
+ " is permanently locked\n",
+ remaining), remaining);
+
+ infoblock = get_prompt_info (app, 3, 0, remaining < 3? remaining : -1);
+
+ /* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
+ the start of the string. Use %0A (single percent) for a linefeed. */
+ prompt = strconcat (_("|A|Please enter the Admin PIN"),
+ "%0A%0A", infoblock, NULL);
+ xfree (infoblock);
+ if (!prompt)
+ return gpg_error_from_syserror ();
+
+ *r_prompt = prompt;
+ return 0;
+}
+
+
+/* Verify CHV3 if required. */
+static gpg_error_t
+verify_chv3 (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc = 0;
+
+ if (!opt.allow_admin)
+ {
+ log_info (_("access to admin commands is not configured\n"));
+ return gpg_error (GPG_ERR_EACCES);
+ }
+
+ if (!app->did_chv3)
+ {
+ pininfo_t pininfo;
+ int minlen = 8;
+ char *prompt;
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = minlen;
+
+ rc = build_enter_admin_pin_prompt (app, &prompt);
+ if (rc)
+ return rc;
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
+ && !check_pinpad_request (app, &pininfo, 1))
+ {
+ /* The reader supports the verify command through the pinpad. */
+ rc = pincb (pincb_arg, prompt, NULL);
+ xfree (prompt);
+ prompt = NULL;
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+ rc = iso7816_verify_kp (app->slot, 0x83, &pininfo);
+ /* Dismiss the prompt. */
+ pincb (pincb_arg, NULL, NULL);
+ }
+ else
+ {
+ char *pinvalue;
+ int pinlen;
+
+ rc = pincb (pincb_arg, prompt, &pinvalue);
+ xfree (prompt);
+ prompt = NULL;
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+
+ if (strlen (pinvalue) < minlen)
+ {
+ log_error (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), 3, minlen);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = pin2hash_if_kdf (app, 3, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_verify (app->slot, 0x83, pinvalue, pinlen);
+ xfree (pinvalue);
+ }
+
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv3 = 1;
+ }
+ return rc;
+}
+
+
+/* Handle the SETATTR operation. All arguments are already basically
+ checked. */
+static gpg_error_t
+do_setattr (app_t app, ctrl_t ctrl, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ gpg_error_t rc;
+ int idx;
+ static struct {
+ const char *name;
+ int tag;
+ int flush_tag; /* The tag which needs to be flushed or 0. */
+ int need_chv;
+ int special;
+ unsigned int need_v2:1;
+ } table[] = {
+ { "DISP-NAME", 0x005B, 0, 3 },
+ { "LOGIN-DATA", 0x005E, 0, 3, 2 },
+ { "DISP-LANG", 0x5F2D, 0, 3 },
+ { "DISP-SEX", 0x5F35, 0, 3 },
+ { "PUBKEY-URL", 0x5F50, 0, 3 },
+ { "CHV-STATUS-1", 0x00C4, 0, 3, 1 },
+ { "CA-FPR-1", 0x00CA, 0x00C6, 3 },
+ { "CA-FPR-2", 0x00CB, 0x00C6, 3 },
+ { "CA-FPR-3", 0x00CC, 0x00C6, 3 },
+ { "PRIVATE-DO-1", 0x0101, 0, 2 },
+ { "PRIVATE-DO-2", 0x0102, 0, 3 },
+ { "PRIVATE-DO-3", 0x0103, 0, 2 },
+ { "PRIVATE-DO-4", 0x0104, 0, 3 },
+ { "CERT-3", 0x7F21, 0, 3, 0, 1 },
+ { "SM-KEY-ENC", 0x00D1, 0, 3, 0, 1 },
+ { "SM-KEY-MAC", 0x00D2, 0, 3, 0, 1 },
+ { "KEY-ATTR", 0, 0, 0, 3, 1 },
+ { "AESKEY", 0x00D5, 0, 3, 0, 1 },
+ { "KDF", 0x00F9, 0, 3, 4, 1 },
+ { NULL, 0 }
+ };
+ int exmode;
+
+ (void)ctrl;
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+ if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */
+
+ if (table[idx].special == 3)
+ return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen);
+
+ switch (table[idx].need_chv)
+ {
+ case 2:
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ break;
+ case 3:
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ break;
+ default:
+ rc = 0;
+ }
+ if (rc)
+ return rc;
+
+ /* Flush the cache before writing it, so that the next get operation
+ will reread the data from the card and thus get synced in case of
+ errors (e.g. data truncated by the card). */
+ flush_cache_item (app, table[idx].flush_tag? table[idx].flush_tag
+ /* */ : table[idx].tag);
+
+ if (app->app_local->cardcap.ext_lc_le && valuelen > 254)
+ exmode = 1; /* Use extended length w/o a limit. */
+ else if (app->app_local->cardcap.cmd_chaining && valuelen > 254)
+ exmode = -254; /* Command chaining with max. 254 bytes. */
+ else
+ exmode = 0;
+ rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen);
+ if (rc)
+ log_error ("failed to set '%s': %s\n", table[idx].name, gpg_strerror (rc));
+
+ if (table[idx].special == 1)
+ app->force_chv1 = (valuelen && *value == 0);
+ else if (table[idx].special == 2)
+ parse_login_data (app);
+ else if (table[idx].special == 4)
+ {
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ app->did_chv3 = 0;
+
+ if ((valuelen == KDF_DATA_LENGTH_MIN || valuelen == KDF_DATA_LENGTH_MAX)
+ && (value[2] == 0x03))
+ app->app_local->pinpad.disabled = 1;
+ else
+ app->app_local->pinpad.disabled = 0;
+ }
+
+ return rc;
+}
+
+
+/* Handle the WRITECERT command for OpenPGP. This rites the standard
+ certifciate to the card; CERTID needs to be set to "OPENPGP.3".
+ PINCB and PINCB_ARG are the usual arguments for the pinentry
+ callback. */
+static gpg_error_t
+do_writecert (app_t app, ctrl_t ctrl,
+ const char *certidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *certdata, size_t certdatalen)
+{
+ if (strcmp (certidstr, "OPENPGP.3"))
+ return gpg_error (GPG_ERR_INV_ID);
+ if (!certdata || !certdatalen)
+ return gpg_error (GPG_ERR_INV_ARG);
+ if (!app->app_local->extcap.is_v2)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+ if (certdatalen > app->app_local->extcap.max_certlen_3)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+ return do_setattr (app, ctrl, "CERT-3", pincb, pincb_arg,
+ certdata, certdatalen);
+}
+
+
+
+/* Handle the PASSWD command. The following combinations are
+ possible:
+
+ Flags CHVNO Vers. Description
+ RESET 1 1 Verify CHV3 and set a new CHV1 and CHV2
+ RESET 1 2 Verify PW3 and set a new PW1.
+ RESET 2 1 Verify CHV3 and set a new CHV1 and CHV2.
+ RESET 2 2 Verify PW3 and set a new Reset Code.
+ RESET 3 any Returns GPG_ERR_INV_ID.
+ - 1 1 Verify CHV2 and set a new CHV1 and CHV2.
+ - 1 2 Verify PW1 and set a new PW1.
+ - 2 1 Verify CHV2 and set a new CHV1 and CHV2.
+ - 2 2 Verify Reset Code and set a new PW1.
+ - 3 any Verify CHV3/PW3 and set a new CHV3/PW3.
+
+ The CHVNO can be prefixed with "OPENPGP.".
+ */
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+ unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc = 0;
+ int chvno;
+ char *resetcode = NULL;
+ char *oldpinvalue = NULL;
+ char *pinvalue = NULL;
+ int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
+ int set_resetcode = 0;
+ pininfo_t pininfo;
+ int use_pinpad = 0;
+ int minlen = 6;
+ int pinlen0 = 0;
+ int pinlen = 0;
+
+ (void)ctrl;
+
+ if (digitp (chvnostr))
+ chvno = atoi (chvnostr);
+ else if (!ascii_strcasecmp (chvnostr, "OPENPGP.1"))
+ chvno = 1;
+ else if (!ascii_strcasecmp (chvnostr, "OPENPGP.2"))
+ chvno = 2;
+ else if (!ascii_strcasecmp (chvnostr, "OPENPGP.3"))
+ chvno = 3;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = minlen;
+
+ if ((flags & APP_CHANGE_FLAG_CLEAR))
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ if (reset_mode && chvno == 3)
+ {
+ rc = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+
+ if (!app->app_local->extcap.is_v2)
+ {
+ /* Version 1 cards. */
+
+ if (reset_mode || chvno == 3)
+ {
+ /* We always require that the PIN is entered. */
+ app->did_chv3 = 0;
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ if (rc)
+ goto leave;
+ }
+ else if (chvno == 1 || chvno == 2)
+ {
+ /* On a v1.x card CHV1 and CVH2 should always have the same
+ value, thus we enforce it here. */
+ int save_force = app->force_chv1;
+
+ app->force_chv1 = 0;
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ app->force_chv1 = save_force;
+ if (rc)
+ goto leave;
+ }
+ else
+ {
+ rc = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+ }
+ else
+ {
+ /* Version 2 cards. */
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot,
+ ISO7816_CHANGE_REFERENCE_DATA, &pininfo)
+ && !check_pinpad_request (app, &pininfo, chvno == 3))
+ use_pinpad = 1;
+
+ if (reset_mode)
+ {
+ /* To reset a PIN the Admin PIN is required. */
+ use_pinpad = 0;
+ app->did_chv3 = 0;
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ if (rc)
+ goto leave;
+
+ if (chvno == 2)
+ set_resetcode = 1;
+ }
+ else if (chvno == 1 || chvno == 3)
+ {
+ if (!use_pinpad)
+ {
+ char *promptbuf = NULL;
+ const char *prompt;
+
+ if (chvno == 3)
+ {
+ minlen = 8;
+ rc = build_enter_admin_pin_prompt (app, &promptbuf);
+ if (rc)
+ goto leave;
+ prompt = promptbuf;
+ }
+ else
+ prompt = _("||Please enter the PIN");
+ rc = pincb (pincb_arg, prompt, &oldpinvalue);
+ xfree (promptbuf);
+ promptbuf = NULL;
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (strlen (oldpinvalue) < minlen)
+ {
+ log_info (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), chvno, minlen);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
+ }
+ }
+ else if (chvno == 2)
+ {
+ /* There is no PW2 for v2 cards. We use this condition to
+ allow a PW reset using the Reset Code. */
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int remaining;
+
+ use_pinpad = 0;
+ minlen = 8;
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ rc = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ remaining = value[5];
+ xfree (relptr);
+ if (!remaining)
+ {
+ log_error (_("Reset Code not or not anymore available\n"));
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
+
+ rc = pincb (pincb_arg,
+ _("||Please enter the Reset Code for the card"),
+ &resetcode);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+ if (strlen (resetcode) < minlen)
+ {
+ log_info (_("Reset Code is too short; minimum length is %d\n"),
+ minlen);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
+ }
+ else
+ {
+ rc = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+ }
+
+ if (chvno == 3)
+ app->did_chv3 = 0;
+ else
+ app->did_chv1 = app->did_chv2 = 0;
+
+ if (!use_pinpad)
+ {
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ rc = pincb (pincb_arg, set_resetcode? _("|RN|New Reset Code") :
+ chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
+ &pinvalue);
+ if (rc || pinvalue == NULL)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+
+ if (resetcode)
+ {
+ char *buffer;
+
+ buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+ if (!buffer)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ strcpy (buffer, resetcode);
+ rc = pin2hash_if_kdf (app, 0, buffer, &pinlen0);
+ if (!rc)
+ {
+ strcpy (buffer+pinlen0, pinvalue);
+ rc = pin2hash_if_kdf (app, 1, buffer+pinlen0, &pinlen);
+ }
+ if (!rc)
+ rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+ buffer, pinlen0+pinlen);
+ wipememory (buffer, pinlen0 + pinlen);
+ xfree (buffer);
+ }
+ }
+ else if (set_resetcode)
+ {
+ if (strlen (pinvalue) < 8)
+ {
+ log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else
+ {
+ rc = pin2hash_if_kdf (app, 0, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_put_data (app->slot, 0, 0xD3, pinvalue, pinlen);
+ }
+ }
+ else if (reset_mode)
+ {
+ rc = pin2hash_if_kdf (app, 1, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_reset_retry_counter (app->slot, 0x81, pinvalue, pinlen);
+ if (!rc && !app->app_local->extcap.is_v2)
+ rc = iso7816_reset_retry_counter (app->slot, 0x82, pinvalue, pinlen);
+ }
+ else if (!app->app_local->extcap.is_v2)
+ {
+ /* Version 1 cards. */
+ if (chvno == 1 || chvno == 2)
+ {
+ rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ if (!rc)
+ rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ }
+ else /* CHVNO == 3 */
+ {
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ }
+ }
+ else
+ {
+ /* Version 2 cards. */
+ assert (chvno == 1 || chvno == 3);
+
+ if (use_pinpad)
+ {
+ rc = pincb (pincb_arg,
+ chvno == 3 ?
+ _("||Please enter the Admin PIN and New Admin PIN") :
+ _("||Please enter the PIN and New PIN"), NULL);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+ rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0,
+ &pininfo);
+ pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+ }
+ else
+ {
+ rc = pin2hash_if_kdf (app, chvno, oldpinvalue, &pinlen0);
+ if (!rc)
+ rc = pin2hash_if_kdf (app, chvno, pinvalue, &pinlen);
+ if (!rc)
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+ oldpinvalue, pinlen0,
+ pinvalue, pinlen);
+ }
+ }
+
+ if (pinvalue)
+ {
+ wipememory (pinvalue, pinlen);
+ xfree (pinvalue);
+ }
+ if (rc)
+ flush_cache_after_error (app);
+
+ leave:
+ if (resetcode)
+ {
+ wipememory (resetcode, strlen (resetcode));
+ xfree (resetcode);
+ }
+ if (oldpinvalue)
+ {
+ wipememory (oldpinvalue, pinlen0);
+ xfree (oldpinvalue);
+ }
+ return rc;
+}
+
+
+/* Check whether a key already exists. KEYIDX is the index of the key
+ (0..2). If FORCE is TRUE a diagnositic will be printed but no
+ error returned if the key already exists. The flag GENERATING is
+ only used to print correct messages. */
+static gpg_error_t
+does_key_exist (app_t app, int keyidx, int generating, int force)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int i;
+
+ assert (keyidx >=0 && keyidx <= 2);
+
+ if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen))
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n < 60)
+ {
+ log_error (_("error reading fingerprint DO\n"));
+ xfree (buffer);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += 20*keyidx;
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ xfree (buffer);
+ if (i!=20 && !force)
+ {
+ log_error (_("key already exists\n"));
+ return gpg_error (GPG_ERR_EEXIST);
+ }
+ else if (i!=20)
+ log_info (_("existing key will be replaced\n"));
+ else if (generating)
+ log_info (_("generating new key\n"));
+ else
+ log_info (_("writing new key\n"));
+ return 0;
+}
+
+
+/* Create a TLV tag and value and store it at BUFFER. Return the length
+ of tag and length. A LENGTH greater than 65535 is truncated. */
+static size_t
+add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
+{
+ unsigned char *p = buffer;
+
+ assert (tag <= 0xffff);
+ if ( tag > 0xff )
+ *p++ = tag >> 8;
+ *p++ = tag;
+ if (length < 128)
+ *p++ = length;
+ else if (length < 256)
+ {
+ *p++ = 0x81;
+ *p++ = length;
+ }
+ else
+ {
+ if (length > 0xffff)
+ length = 0xffff;
+ *p++ = 0x82;
+ *p++ = length >> 8;
+ *p++ = length;
+ }
+
+ return p - buffer;
+}
+
+
+static gpg_error_t
+build_privkey_template (app_t app, int keyno,
+ const unsigned char *rsa_n, size_t rsa_n_len,
+ const unsigned char *rsa_e, size_t rsa_e_len,
+ const unsigned char *rsa_p, size_t rsa_p_len,
+ const unsigned char *rsa_q, size_t rsa_q_len,
+ const unsigned char *rsa_u, size_t rsa_u_len,
+ const unsigned char *rsa_dp, size_t rsa_dp_len,
+ const unsigned char *rsa_dq, size_t rsa_dq_len,
+ unsigned char **result, size_t *resultlen)
+{
+ size_t rsa_e_reqlen;
+ unsigned char privkey[7*(1+3+3)];
+ size_t privkey_len;
+ unsigned char exthdr[2+2+3];
+ size_t exthdr_len;
+ unsigned char suffix[2+3];
+ size_t suffix_len;
+ unsigned char *tp;
+ size_t datalen;
+ unsigned char *template;
+ size_t template_size;
+
+ *result = NULL;
+ *resultlen = 0;
+
+ switch (app->app_local->keyattr[keyno].rsa.format)
+ {
+ case RSA_STD:
+ case RSA_STD_N:
+ case RSA_CRT:
+ case RSA_CRT_N:
+ break;
+
+ default:
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ /* Get the required length for E. Rounded up to the nearest byte */
+ rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8;
+ assert (rsa_e_len <= rsa_e_reqlen);
+
+ /* Build the 7f48 cardholder private key template. */
+ datalen = 0;
+ tp = privkey;
+
+ tp += add_tlv (tp, 0x91, rsa_e_reqlen);
+ datalen += rsa_e_reqlen;
+
+ tp += add_tlv (tp, 0x92, rsa_p_len);
+ datalen += rsa_p_len;
+
+ tp += add_tlv (tp, 0x93, rsa_q_len);
+ datalen += rsa_q_len;
+
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+ {
+ tp += add_tlv (tp, 0x94, rsa_u_len);
+ datalen += rsa_u_len;
+ tp += add_tlv (tp, 0x95, rsa_dp_len);
+ datalen += rsa_dp_len;
+ tp += add_tlv (tp, 0x96, rsa_dq_len);
+ datalen += rsa_dq_len;
+ }
+
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+ {
+ tp += add_tlv (tp, 0x97, rsa_n_len);
+ datalen += rsa_n_len;
+ }
+ privkey_len = tp - privkey;
+
+ /* Build the extended header list without the private key template. */
+ tp = exthdr;
+ *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
+ *tp++ = 0;
+ tp += add_tlv (tp, 0x7f48, privkey_len);
+ exthdr_len = tp - exthdr;
+
+ /* Build the 5f48 suffix of the data. */
+ tp = suffix;
+ tp += add_tlv (tp, 0x5f48, datalen);
+ suffix_len = tp - suffix;
+
+ /* Now concatenate everything. */
+ template_size = (1 + 3 /* 0x4d and len. */
+ + exthdr_len
+ + privkey_len
+ + suffix_len
+ + datalen);
+ tp = template = xtrymalloc_secure (template_size);
+ if (!template)
+ return gpg_error_from_syserror ();
+
+ tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+ memcpy (tp, exthdr, exthdr_len);
+ tp += exthdr_len;
+ memcpy (tp, privkey, privkey_len);
+ tp += privkey_len;
+ memcpy (tp, suffix, suffix_len);
+ tp += suffix_len;
+
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < rsa_e_reqlen)
+ {
+ /* Right justify E. */
+ memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
+ memset (tp, 0, rsa_e_reqlen - rsa_e_len);
+ }
+ tp += rsa_e_reqlen;
+
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+ {
+ memcpy (tp, rsa_u, rsa_u_len);
+ tp += rsa_u_len;
+ memcpy (tp, rsa_dp, rsa_dp_len);
+ tp += rsa_dp_len;
+ memcpy (tp, rsa_dq, rsa_dq_len);
+ tp += rsa_dq_len;
+ }
+
+ if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
+ || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
+ {
+ memcpy (tp, rsa_n, rsa_n_len);
+ tp += rsa_n_len;
+ }
+
+ /* Sanity check. We don't know the exact length because we
+ allocated 3 bytes for the first length header. */
+ assert (tp - template <= template_size);
+
+ *result = template;
+ *resultlen = tp - template;
+ return 0;
+}
+
+static gpg_error_t
+build_ecc_privkey_template (app_t app, int keyno,
+ const unsigned char *ecc_d, size_t ecc_d_len,
+ size_t ecc_d_fixed_len,
+ const unsigned char *ecc_q, size_t ecc_q_len,
+ unsigned char **result, size_t *resultlen)
+{
+ unsigned char privkey[2*(1+3)];
+ size_t privkey_len;
+ unsigned char exthdr[2+2+3];
+ size_t exthdr_len;
+ unsigned char suffix[2+3];
+ size_t suffix_len;
+ unsigned char *tp;
+ size_t datalen;
+ unsigned char *template;
+ size_t template_size;
+ int pubkey_required;
+
+ /* This case doesn't occur in GnuPG 2.3 or later, because
+ agent/sexp-secret.c does the fixup. */
+ if (ecc_d_fixed_len < ecc_d_len)
+ {
+ if (ecc_d_fixed_len != ecc_d_len - 1 || *ecc_d)
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ /* Remove the additional zero. */
+ ecc_d_len--;
+ ecc_d++;
+ }
+
+ pubkey_required = !!(app->app_local->keyattr[keyno].ecc.flags
+ & ECC_FLAG_PUBKEY);
+
+ *result = NULL;
+ *resultlen = 0;
+
+ /* Build the 7f48 cardholder private key template. */
+ datalen = 0;
+ tp = privkey;
+
+ tp += add_tlv (tp, 0x92, ecc_d_fixed_len);
+ datalen += ecc_d_fixed_len;
+
+ if (pubkey_required)
+ {
+ tp += add_tlv (tp, 0x99, ecc_q_len);
+ datalen += ecc_q_len;
+ }
+
+ privkey_len = tp - privkey;
+
+
+ /* Build the extended header list without the private key template. */
+ tp = exthdr;
+ *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
+ *tp++ = 0;
+ tp += add_tlv (tp, 0x7f48, privkey_len);
+ exthdr_len = tp - exthdr;
+
+ /* Build the 5f48 suffix of the data. */
+ tp = suffix;
+ tp += add_tlv (tp, 0x5f48, datalen);
+ suffix_len = tp - suffix;
+
+ /* Now concatenate everything. */
+ template_size = (1 + 1 /* 0x4d and len. */
+ + exthdr_len
+ + privkey_len
+ + suffix_len
+ + datalen);
+ if (exthdr_len + privkey_len + suffix_len + datalen >= 128)
+ template_size++;
+ tp = template = xtrymalloc_secure (template_size);
+ if (!template)
+ return gpg_error_from_syserror ();
+
+ tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+ memcpy (tp, exthdr, exthdr_len);
+ tp += exthdr_len;
+ memcpy (tp, privkey, privkey_len);
+ tp += privkey_len;
+ memcpy (tp, suffix, suffix_len);
+ tp += suffix_len;
+
+ if (ecc_d_fixed_len > ecc_d_len)
+ {
+ memset (tp, 0, ecc_d_fixed_len - ecc_d_len);
+ memcpy (tp + ecc_d_fixed_len - ecc_d_len, ecc_d, ecc_d_len);
+ }
+ else
+ memcpy (tp, ecc_d, ecc_d_len);
+ tp += ecc_d_fixed_len;
+
+ if (pubkey_required)
+ {
+ memcpy (tp, ecc_q, ecc_q_len);
+ tp += ecc_q_len;
+ }
+
+ assert (tp - template == template_size);
+
+ *result = template;
+ *resultlen = tp - template;
+ return 0;
+}
+
+
+/* Helper for do_writekey to change the size of a key. Note that
+ this deletes the entire key without asking. */
+static gpg_error_t
+change_keyattr (app_t app, int keyno, const unsigned char *buf, size_t buflen,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+
+ assert (keyno >=0 && keyno <= 2);
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ return err;
+
+ /* Change the attribute. */
+ err = iso7816_put_data (app->slot, 0, 0xC1+keyno, buf, buflen);
+ if (err)
+ log_error ("error changing key attribute (key=%d)\n", keyno+1);
+ else
+ log_info ("key attribute changed (key=%d)\n", keyno+1);
+ flush_cache (app);
+ err = parse_algorithm_attribute (app, keyno);
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ app->did_chv3 = 0;
+ return err;
+}
+
+
+static gpg_error_t
+change_rsa_keyattr (app_t app, int keyno, unsigned int nbits,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err = 0;
+ unsigned char *buf;
+ size_t buflen;
+ void *relptr;
+
+ /* Read the current attributes into a buffer. */
+ relptr = get_one_do (app, 0xC1+keyno, &buf, &buflen, NULL);
+ if (!relptr)
+ err = gpg_error (GPG_ERR_CARD);
+ else if (buflen < 6)
+ {
+ /* Attributes too short. */
+ xfree (relptr);
+ err = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ /* If key attribute was RSA, we only change n_bits and don't
+ touch anything else. Before we do so, we round up NBITS to a
+ sensible way in the same way as gpg's key generation does it.
+ This may help to sort out problems with a few bits too short
+ keys. */
+ nbits = ((nbits + 31) / 32) * 32;
+ buf[1] = (nbits >> 8);
+ buf[2] = nbits;
+
+ /* If it was not RSA, we need to fill other parts. */
+ if (buf[0] != PUBKEY_ALGO_RSA)
+ {
+ buf[0] = PUBKEY_ALGO_RSA;
+ buf[3] = 0;
+ buf[4] = 32;
+ buf[5] = 0;
+ buflen = 6;
+ }
+
+ err = change_keyattr (app, keyno, buf, buflen, pincb, pincb_arg);
+ xfree (relptr);
+ }
+
+ return err;
+}
+
+
+/* Helper to process an setattr command for name KEY-ATTR.
+ In (VALUE,VALUELEN), it expects following string:
+ RSA: "--force <key> <algo> rsa<nbits>"
+ ECC: "--force <key> <algo> <curvename>"
+ */
+static gpg_error_t
+change_keyattr_from_string (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *value, size_t valuelen)
+{
+ gpg_error_t err = 0;
+ char *string;
+ int key, keyno, algo;
+ int n = 0;
+
+ /* VALUE is expected to be a string but not guaranteed to be
+ terminated. Thus copy it to an allocated buffer first. */
+ string = xtrymalloc (valuelen+1);
+ if (!string)
+ return gpg_error_from_syserror ();
+ memcpy (string, value, valuelen);
+ string[valuelen] = 0;
+
+ /* Because this function deletes the key we require the string
+ "--force" in the data to make clear that something serious might
+ happen. */
+ sscanf (string, "--force %d %d %n", &key, &algo, &n);
+ if (n < 12)
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ keyno = key - 1;
+ if (keyno < 0 || keyno > 2)
+ err = gpg_error (GPG_ERR_INV_ID);
+ else if (algo == PUBKEY_ALGO_RSA)
+ {
+ unsigned int nbits;
+
+ errno = 0;
+ nbits = strtoul (string+n+3, NULL, 10);
+ if (errno)
+ err = gpg_error (GPG_ERR_INV_DATA);
+ else if (nbits < 1024)
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ else if (nbits > 4096)
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ else
+ err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
+ }
+ else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA
+ || algo == PUBKEY_ALGO_EDDSA)
+ {
+ const char *oidstr;
+ gcry_mpi_t oid;
+ const unsigned char *oidbuf;
+ size_t oid_len;
+
+ oidstr = openpgp_curve_to_oid (string+n, NULL, NULL);
+ if (!oidstr)
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ err = openpgp_oid_from_str (oidstr, &oid);
+ if (err)
+ goto leave;
+
+ oidbuf = gcry_mpi_get_opaque (oid, &n);
+ oid_len = (n+7)/8;
+
+ /* We have enough room at STRING. */
+ string[0] = algo;
+ memcpy (string+1, oidbuf+1, oid_len-1);
+ err = change_keyattr (app, keyno, string, oid_len, pincb, pincb_arg);
+ gcry_mpi_release (oid);
+ }
+ else
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+
+ leave:
+ xfree (string);
+ return err;
+}
+
+
+static gpg_error_t
+rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg, int keyno,
+ const unsigned char *buf, size_t buflen, int depth)
+{
+ gpg_error_t err;
+ const unsigned char *tok;
+ size_t toklen;
+ int last_depth1, last_depth2;
+ const unsigned char *rsa_n = NULL;
+ const unsigned char *rsa_e = NULL;
+ const unsigned char *rsa_p = NULL;
+ const unsigned char *rsa_q = NULL;
+ size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned int nbits;
+ unsigned int maxbits;
+ unsigned char *template = NULL;
+ unsigned char *tp;
+ size_t template_len;
+ unsigned char fprbuf[20];
+ u32 created_at = 0;
+
+ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_RSA)
+ {
+ log_error (_("unsupported algorithm: %s"), "RSA");
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 1)
+ {
+ const unsigned char **mpi;
+ size_t *mpi_len;
+
+ switch (*tok)
+ {
+ case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
+ case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
+ case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
+ case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+ if (mpi && *mpi)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && mpi)
+ {
+ /* Strip off leading zero bytes and save. */
+ for (;toklen && !*tok; toklen--, tok++)
+ ;
+ *mpi = tok;
+ *mpi_len = toklen;
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+ /* Parse other attributes. */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+ {
+ if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+ goto leave;
+ if (tok)
+ {
+ for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+ tok++, toklen--)
+ created_at = created_at*10 + (*tok - '0');
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+ if (!created_at)
+ {
+ log_error (_("creation timestamp missing\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
+ nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
+ if (opt.verbose)
+ log_info ("RSA modulus size is %u bits\n", nbits);
+ if (nbits && nbits != maxbits
+ && app->app_local->extcap.algo_attr_change)
+ {
+ /* Try to switch the key to a new length. */
+ err = change_rsa_keyattr (app, keyno, nbits, pincb, pincb_arg);
+ if (!err)
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
+ }
+ if (nbits != maxbits)
+ {
+ log_error (_("RSA modulus missing or not of size %d bits\n"),
+ (int)maxbits);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ maxbits = app->app_local->keyattr[keyno].rsa.e_bits;
+ if (maxbits > 32 && !app->app_local->extcap.is_v2)
+ maxbits = 32; /* Our code for v1 does only support 32 bits. */
+ nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
+ if (nbits < 2 || nbits > maxbits)
+ {
+ log_error (_("RSA public exponent missing or larger than %d bits\n"),
+ (int)maxbits);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ maxbits = app->app_local->keyattr[keyno].rsa.n_bits/2;
+ nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
+ if (nbits != maxbits)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ "P", (int)maxbits);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
+ if (nbits != maxbits)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ "Q", (int)maxbits);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+ /* We need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+
+ if (app->app_local->extcap.is_v2)
+ {
+ unsigned char *rsa_u, *rsa_dp, *rsa_dq;
+ size_t rsa_u_len, rsa_dp_len, rsa_dq_len;
+ gcry_mpi_t mpi_e, mpi_p, mpi_q;
+ gcry_mpi_t mpi_u = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_dp = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_dq = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_tmp = gcry_mpi_snew (0);
+ int exmode;
+
+ /* Calculate the u, dp and dq components needed by RSA_CRT cards */
+ gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL);
+ gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL);
+ gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL);
+
+ gcry_mpi_invm (mpi_u, mpi_q, mpi_p);
+ gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1);
+ gcry_mpi_invm (mpi_dp, mpi_e, mpi_tmp);
+ gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1);
+ gcry_mpi_invm (mpi_dq, mpi_e, mpi_tmp);
+
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_u, &rsa_u_len, mpi_u);
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dp, &rsa_dp_len, mpi_dp);
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dq, &rsa_dq_len, mpi_dq);
+
+ gcry_mpi_release (mpi_e);
+ gcry_mpi_release (mpi_p);
+ gcry_mpi_release (mpi_q);
+ gcry_mpi_release (mpi_u);
+ gcry_mpi_release (mpi_dp);
+ gcry_mpi_release (mpi_dq);
+ gcry_mpi_release (mpi_tmp);
+
+ /* Build the private key template as described in section 4.3.3.7 of
+ the OpenPGP card specs version 2.0. */
+ err = build_privkey_template (app, keyno,
+ rsa_n, rsa_n_len,
+ rsa_e, rsa_e_len,
+ rsa_p, rsa_p_len,
+ rsa_q, rsa_q_len,
+ rsa_u, rsa_u_len,
+ rsa_dp, rsa_dp_len,
+ rsa_dq, rsa_dq_len,
+ &template, &template_len);
+ xfree(rsa_u);
+ xfree(rsa_dp);
+ xfree(rsa_dq);
+
+ if (err)
+ goto leave;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ if (app->app_local->cardcap.ext_lc_le && template_len > 254)
+ exmode = 1; /* Use extended length w/o a limit. */
+ else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+ exmode = -254;
+ else
+ exmode = 0;
+ err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+ template, template_len);
+ }
+ else
+ {
+ /* Build the private key template as described in section 4.3.3.6 of
+ the OpenPGP card specs version 1.1:
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
+ */
+ assert (rsa_e_len <= 4);
+ template_len = (1 + 1 + 4
+ + 1 + 1 + rsa_p_len
+ + 1 + 1 + rsa_q_len);
+ template = tp = xtrymalloc_secure (template_len);
+ if (!template)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < 4)
+ {
+ /* Right justify E. */
+ memmove (tp+4-rsa_e_len, tp, rsa_e_len);
+ memset (tp, 0, 4-rsa_e_len);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = rsa_p_len;
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ *tp++ = 0xC2;
+ *tp++ = rsa_q_len;
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ assert (tp - template == template_len);
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ err = iso7816_put_data (app->slot, 0,
+ (app->appversion > 0x0007? 0xE0:0xE9)+keyno,
+ template, template_len);
+ }
+ if (err)
+ {
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = store_fpr (app, keyno, created_at, fprbuf, PUBKEY_ALGO_RSA,
+ rsa_n, rsa_n_len, rsa_e, rsa_e_len);
+ if (err)
+ goto leave;
+
+
+ leave:
+ xfree (template);
+ return err;
+}
+
+
+static gpg_error_t
+ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg, int keyno,
+ const unsigned char *buf, size_t buflen, int depth)
+{
+ gpg_error_t err;
+ const unsigned char *tok;
+ size_t toklen;
+ int last_depth1, last_depth2;
+ const unsigned char *ecc_q = NULL;
+ const unsigned char *ecc_d = NULL;
+ size_t ecc_q_len, ecc_d_len;
+ const char *curve = NULL;
+ u32 created_at = 0;
+ const char *oidstr;
+ int flag_djb_tweak = 0;
+ int algo;
+ gcry_mpi_t oid = NULL;
+ const unsigned char *oidbuf;
+ unsigned int n;
+ size_t oid_len;
+ unsigned char fprbuf[20];
+ size_t ecc_d_fixed_len;
+
+ /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
+ curve = "NIST P-256" */
+ /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
+ curve = "secp256k1" */
+ /* (private-key(ecc(curve%s)(flags eddsa)(q%m)(d%m))(created-at%d)):
+ curve = "Ed25519" */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+
+ if (tok && toklen == 5 && !memcmp (tok, "curve", 5))
+ {
+ char *curve_name;
+
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+
+ curve_name = xtrymalloc (toklen+1);
+ if (!curve_name)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ memcpy (curve_name, tok, toklen);
+ curve_name[toklen] = 0;
+ curve = openpgp_is_curve_supported (curve_name, NULL, NULL);
+ xfree (curve_name);
+ }
+ else if (tok && toklen == 5 && !memcmp (tok, "flags", 5))
+ {
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+
+ if (tok)
+ {
+ if ((toklen == 5 && !memcmp (tok, "eddsa", 5))
+ || (toklen == 9 && !memcmp (tok, "djb-tweak", 9)))
+ flag_djb_tweak = 1;
+ }
+ }
+ else if (tok && toklen == 1)
+ {
+ const unsigned char **buf2;
+ size_t *buf2len;
+ int native = flag_djb_tweak;
+
+ switch (*tok)
+ {
+ case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
+ case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; native = 0; break;
+ default: buf2 = NULL; buf2len = NULL; break;
+ }
+ if (buf2 && *buf2)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && buf2)
+ {
+ if (!native)
+ /* Strip off leading zero bytes and save. */
+ for (;toklen && !*tok; toklen--, tok++)
+ ;
+
+ *buf2 = tok;
+ *buf2len = toklen;
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+ /* Parse other attributes. */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+ {
+ if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+ goto leave;
+ if (tok)
+ {
+ for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+ tok++, toklen--)
+ created_at = created_at*10 + (*tok - '0');
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+ if (!curve)
+ {
+ log_error (_("unsupported curve\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ if (!created_at)
+ {
+ log_error (_("creation timestamp missing\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ if (flag_djb_tweak && keyno != 1)
+ algo = PUBKEY_ALGO_EDDSA;
+ else if (keyno == 1)
+ algo = PUBKEY_ALGO_ECDH;
+ else
+ algo = PUBKEY_ALGO_ECDSA;
+
+ oidstr = openpgp_curve_to_oid (curve, &n, NULL);
+ ecc_d_fixed_len = (n+7)/8;
+ err = openpgp_oid_from_str (oidstr, &oid);
+ if (err)
+ goto leave;
+ oidbuf = gcry_mpi_get_opaque (oid, &n);
+ if (!oidbuf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ oid_len = (n+7)/8;
+
+ if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_ECC
+ || app->app_local->keyattr[keyno].ecc.curve != curve
+ || (flag_djb_tweak !=
+ (app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)))
+ {
+ if (app->app_local->extcap.algo_attr_change)
+ {
+ unsigned char *keyattr;
+
+ if (!oid_len)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ keyattr = xtrymalloc (oid_len);
+ if (!keyattr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ keyattr[0] = algo;
+ memcpy (keyattr+1, oidbuf+1, oid_len-1);
+ err = change_keyattr (app, keyno, keyattr, oid_len, pincb, pincb_arg);
+ xfree (keyattr);
+ if (err)
+ goto leave;
+ }
+ else
+ {
+ log_error ("key attribute on card doesn't match\n");
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ }
+
+ if (opt.verbose)
+ log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len);
+
+ /* We need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ if (app->app_local->extcap.is_v2)
+ {
+ /* Build the private key template as described in section 4.3.3.7 of
+ the OpenPGP card specs version 2.0. */
+ unsigned char *template;
+ size_t template_len;
+ int exmode;
+
+ err = build_ecc_privkey_template (app, keyno,
+ ecc_d, ecc_d_len, ecc_d_fixed_len,
+ ecc_q, ecc_q_len,
+ &template, &template_len);
+ if (err)
+ goto leave;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ {
+ xfree (template);
+ goto leave;
+ }
+
+ /* Store the key. */
+ if (app->app_local->cardcap.ext_lc_le && template_len > 254)
+ exmode = 1; /* Use extended length w/o a limit. */
+ else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+ exmode = -254;
+ else
+ exmode = 0;
+ err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+ template, template_len);
+ xfree (template);
+ }
+ else
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if (err)
+ {
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = store_fpr (app, keyno, created_at, fprbuf, algo, oidbuf, oid_len,
+ ecc_q, ecc_q_len, ecdh_params (curve), (size_t)4);
+
+ leave:
+ gcry_mpi_release (oid);
+ return err;
+}
+
+/* Handle the WRITEKEY command for OpenPGP. This function expects a
+ canonical encoded S-expression with the secret key in KEYDATA and
+ its length (for assertions) in KEYDATALEN. KEYID needs to be the
+ usual keyid which for OpenPGP is the string "OPENPGP.n" with
+ n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall
+ get overwritten. PINCB and PINCB_ARG are the usual arguments for
+ the pinentry callback. */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+ int force = (flags & 1);
+ int keyno;
+ const unsigned char *buf, *tok;
+ size_t buflen, toklen;
+ int depth;
+
+ (void)ctrl;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 0;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 2;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = does_key_exist (app, keyno, 0, force);
+ if (err)
+ return err;
+
+
+ /*
+ Parse the S-expression
+ */
+ buf = keydata;
+ buflen = keydatalen;
+ depth = 0;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+ {
+ if (!tok)
+ ;
+ else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+ log_info ("protected-private-key passed to writekey\n");
+ else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+ log_info ("shadowed-private-key passed to writekey\n");
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 3 && memcmp ("rsa", tok, toklen) == 0)
+ err = rsa_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
+ else if (tok && toklen == 3 && memcmp ("ecc", tok, toklen) == 0)
+ err = ecc_writekey (app, pincb, pincb_arg, keyno, buf, buflen, depth);
+ else
+ {
+ err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
+
+/* Handle the GENKEY command. */
+static gpg_error_t
+do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, const char *keytype,
+ unsigned int flags, time_t createtime,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ char numbuf[30];
+ unsigned char *buffer = NULL;
+ const unsigned char *keydata;
+ size_t buflen, keydatalen;
+ u32 created_at;
+ int keyno = atoi (keynostr) - 1;
+ int force = (flags & 1);
+ time_t start_at;
+ int exmode = 0;
+ int le_value = 256; /* Use legacy value. */
+
+ (void)keytype; /* Ignored for OpenPGP cards. */
+
+ if (keyno < 0 || keyno > 2)
+ return gpg_error (GPG_ERR_INV_ID);
+
+ /* We flush the cache to increase the traffic before a key
+ generation. This _might_ help a card to gather more entropy. */
+ flush_cache (app);
+
+ /* Obviously we need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ /* Check whether a key already exists. */
+ err = does_key_exist (app, keyno, 1, force);
+ if (err)
+ return err;
+
+ if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+ {
+ unsigned int keybits = app->app_local->keyattr[keyno].rsa.n_bits;
+
+ /* Because we send the key parameter back via status lines we need
+ to put a limit on the max. allowed keysize. 2048 bit will
+ already lead to a 527 byte long status line and thus a 4096 bit
+ key would exceed the Assuan line length limit. */
+ if (keybits > 4096)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ if (app->app_local->cardcap.ext_lc_le && keybits > RSA_SMALL_SIZE_KEY
+ && app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
+ {
+ exmode = 1; /* Use extended length w/o a limit. */
+ le_value = determine_rsa_response (app, keyno);
+ /* No need to check le_value because it comes from a 16 bit
+ value and thus can't create an overflow on a 32 bit
+ system. */
+ }
+ }
+
+ /* Prepare for key generation by verifying the Admin PIN. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ return err;
+
+
+ log_info (_("please wait while key is being generated ...\n"));
+ start_at = time (NULL);
+ err = iso7816_generate_keypair (app->slot, exmode, 0x80, 0,
+ (keyno == 0? "\xB6" :
+ keyno == 1? "\xB8" : "\xA4"),
+ 2, le_value, &buffer, &buflen);
+ if (err)
+ {
+ log_error (_("generating key failed\n"));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ {
+ int nsecs = (int)(time (NULL) - start_at);
+ log_info (ngettext("key generation completed (%d second)\n",
+ "key generation completed (%d seconds)\n",
+ nsecs), nsecs);
+ }
+
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+ if (!keydata)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the public key data\n"));
+ goto leave;
+ }
+
+ created_at = (u32)(createtime? createtime : gnupg_get_time ());
+ sprintf (numbuf, "%u", created_at);
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+ err = read_public_key (app, ctrl, created_at, keyno, buffer, buflen);
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+static unsigned long
+convert_sig_counter_value (const unsigned char *value, size_t valuelen)
+{
+ unsigned long ul;
+
+ if (valuelen == 3 )
+ ul = (value[0] << 16) | (value[1] << 8) | value[2];
+ else
+ {
+ log_error (_("invalid structure of OpenPGP card (DO 0x93)\n"));
+ ul = 0;
+ }
+ return ul;
+}
+
+static unsigned long
+get_sig_counter (app_t app)
+{
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ unsigned long ul;
+
+ relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL);
+ if (!relptr)
+ return 0;
+ ul = convert_sig_counter_value (value, valuelen);
+ xfree (relptr);
+ return ul;
+}
+
+static gpg_error_t
+compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int rc, i;
+
+ assert (keyno >= 0 && keyno <= 2);
+
+ rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
+ if (rc)
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n < 60)
+ {
+ xfree (buffer);
+ log_error (_("error reading fingerprint DO\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += keyno*20;
+ for (i=0; i < 20; i++)
+ if (sha1fpr[i] != fpr[i])
+ {
+ xfree (buffer);
+ log_info (_("fingerprint on card does not match requested one\n"));
+ return gpg_error (GPG_ERR_WRONG_SECKEY);
+ }
+ xfree (buffer);
+ return 0;
+}
+
+
+/* If a fingerprint has been specified check it against the one on the
+ card. This allows for a meaningful error message in case the key
+ on the card has been replaced but the shadow information known to
+ gpg has not been updated. If there is no fingerprint we assume
+ that this is okay. */
+static gpg_error_t
+check_against_given_fingerprint (app_t app, const char *fpr, int key)
+{
+ unsigned char tmp[20];
+ const char *s;
+ int n;
+
+ for (s=fpr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 40)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* okay */
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=fpr, n=0; n < 20; s += 2, n++)
+ tmp[n] = xtoi_2 (s);
+ return compare_fingerprint (app, key-1, tmp);
+}
+
+
+/* Check KEYIDSTR, if it's valid.
+ When KEYNO is 0, it means it's for PIN check.
+ Otherwise, KEYNO corresponds to the slot (signing, decipher and auth).
+ KEYIDSTR is either:
+ (1) Serial number
+ (2) Serial number "/" fingerprint
+ (3) Serial number "[CHV3]"
+ (4) keygrip
+
+ When KEYNO is 0 and KEYIDSTR is for a keygrip, the keygrip should
+ be to be compared is the first one (keygrip for signing).
+ When KEYNO is 1, KEYIDSTR is for a keygrip, and R_USE_AUTH is not
+ NULL, OpenPGP.1 is first tested and then OpenPGP.3. In the latter
+ case 1 is stored at R_USE_AUTH
+ */
+static int
+check_keyidstr (app_t app, const char *keyidstr, int keyno, int *r_use_auth)
+{
+ int rc;
+ const char *s;
+ int n;
+ const char *fpr = NULL;
+ int i;
+
+ if (r_use_auth)
+ *r_use_auth = 0;
+
+ /* Make sure we have load the public keys. */
+ for (i = 0; i < 3; i++)
+ get_public_key (app, i);
+
+ if (strlen (keyidstr) < 32)
+ return gpg_error (GPG_ERR_INV_ID);
+ else
+ {
+ char *serial;
+
+ for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+ ;
+
+ /* Check if it's a keygrip */
+ if (n == 40)
+ {
+ const unsigned char *keygrip_str;
+
+ keygrip_str = app->app_local->pk[keyno?keyno-1:0].keygrip_str;
+ if (!strncmp (keygrip_str, keyidstr, 40))
+ return 0;
+ else if (keyno == 1 && r_use_auth
+ && !strncmp (app->app_local->pk[2].keygrip_str,
+ keyidstr, 40))
+ {
+ *r_use_auth = 1;
+ return 0;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+ }
+
+ if (n != 32 || strncmp (keyidstr, "D27600012401", 12))
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* no fingerprint given: we allow this for now. */
+ else if (*s == '/')
+ fpr = s + 1;
+
+ serial = app_get_serialno (app);
+ if (strncmp (serial, keyidstr, 32))
+ {
+ xfree (serial);
+ return gpg_error (GPG_ERR_WRONG_CARD);
+ }
+
+ xfree (serial);
+ }
+
+ /* If a fingerprint has been specified check it against the one on
+ the card. This is allows for a meaningful error message in case
+ the key on the card has been replaced but the shadow information
+ known to gpg was not updated. If there is no fingerprint, gpg
+ will detect a bogus signature anyway due to the
+ verify-after-signing feature. */
+ rc = (fpr&&keyno)? check_against_given_fingerprint (app, fpr, keyno) : 0;
+
+ return rc;
+}
+
+
+/* Compute a digital signature on INDATA which is expected to be the
+ raw message digest. For this application the KEYIDSTR consists of
+ the serialnumber and the fingerprint delimited by a slash.
+
+ Note that this function may return the error code
+ GPG_ERR_WRONG_CARD to indicate that the card currently present does
+ not match the one required for the requested action (e.g. the
+ serial number does not match).
+
+ As a special feature a KEYIDSTR of "OPENPGP.3" redirects the
+ operation to the auth command.
+*/
+static gpg_error_t
+do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
+ { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
+ 0x1C };
+ static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
+ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20 };
+ static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
+ { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+ 0x00, 0x04, 0x30 };
+ static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
+ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40 };
+ int rc;
+ unsigned char data[19+64];
+ size_t datalen;
+ unsigned long sigcount;
+ int use_auth = 0;
+ int exmode, le_value;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Strip off known prefixes. */
+#define X(a,b,c,d) \
+ if (hashalgo == GCRY_MD_ ## a \
+ && (d) \
+ && indatalen == sizeof b ## _prefix + (c) \
+ && !memcmp (indata, b ## _prefix, sizeof b ## _prefix)) \
+ { \
+ indata = (const char*)indata + sizeof b ## _prefix; \
+ indatalen -= sizeof b ## _prefix; \
+ }
+
+ if (indatalen == 20)
+ ; /* Assume a plain SHA-1 or RMD160 digest has been given. */
+ else X(SHA1, sha1, 20, 1)
+ else X(RMD160, rmd160, 20, 1)
+ else X(SHA224, sha224, 28, app->app_local->extcap.is_v2)
+ else X(SHA256, sha256, 32, app->app_local->extcap.is_v2)
+ else X(SHA384, sha384, 48, app->app_local->extcap.is_v2)
+ else X(SHA512, sha512, 64, app->app_local->extcap.is_v2)
+ else if ((indatalen == 28 || indatalen == 32
+ || indatalen == 48 || indatalen ==64)
+ && app->app_local->extcap.is_v2)
+ ; /* Assume a plain SHA-3 digest has been given. */
+ else
+ {
+ log_error (_("card does not support digest algorithm %s\n"),
+ gcry_md_algo_name (hashalgo));
+ /* Or the supplied digest length does not match an algorithm. */
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+#undef X
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (!strcmp (keyidstr, "OPENPGP.1"))
+ ;
+ else if (!strcmp (keyidstr, "OPENPGP.3"))
+ use_auth = 1;
+ else
+ {
+ rc = check_keyidstr (app, keyidstr, 1, &use_auth);
+ if (rc)
+ return rc;
+ }
+
+ /* Concatenate prefix and digest. */
+#define X(a,b,d) \
+ if (hashalgo == GCRY_MD_ ## a && (d) ) \
+ { \
+ datalen = sizeof b ## _prefix + indatalen; \
+ assert (datalen <= sizeof data); \
+ memcpy (data, b ## _prefix, sizeof b ## _prefix); \
+ memcpy (data + sizeof b ## _prefix, indata, indatalen); \
+ }
+
+ if (use_auth
+ || app->app_local->keyattr[use_auth? 2: 0].key_type == KEY_TYPE_RSA)
+ {
+ X(SHA1, sha1, 1)
+ else X(RMD160, rmd160, 1)
+ else X(SHA224, sha224, app->app_local->extcap.is_v2)
+ else X(SHA256, sha256, app->app_local->extcap.is_v2)
+ else X(SHA384, sha384, app->app_local->extcap.is_v2)
+ else X(SHA512, sha512, app->app_local->extcap.is_v2)
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ }
+ else
+ {
+ datalen = indatalen;
+ memcpy (data, indata, indatalen);
+ }
+#undef X
+
+ /* Redirect to the AUTH command if asked to. */
+ if (use_auth)
+ {
+ return do_auth (app, ctrl, "OPENPGP.3", pincb, pincb_arg,
+ data, datalen,
+ outdata, outdatalen);
+ }
+
+ /* Show the number of signature done using this key. */
+ sigcount = get_sig_counter (app);
+ log_info (_("signatures created so far: %lu\n"), sigcount);
+
+ /* Check CHV if needed. */
+ if (!app->did_chv1 || app->force_chv1)
+ {
+ char *pinvalue;
+ int pinlen;
+
+ rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue, &pinlen);
+ if (rc)
+ return rc;
+
+ app->did_chv1 = 1;
+
+ /* For cards with versions < 2 we want to keep CHV1 and CHV2 in
+ sync, thus we verify CHV2 here using the given PIN. Cards
+ with version2 to not have the need for a separate CHV2 and
+ internally use just one. Obviously we can't do that if the
+ pinpad has been used. */
+ if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
+ {
+ rc = iso7816_verify (app->slot, 0x82, pinvalue, pinlen);
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
+ xfree (pinvalue);
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv2 = 1;
+ }
+ xfree (pinvalue);
+ }
+
+
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[0].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[0].rsa.n_bits > RSA_SMALL_SIZE_OP)
+ {
+ exmode = 1; /* Use extended length. */
+ le_value = app->app_local->keyattr[0].rsa.n_bits / 8;
+ }
+ else
+ {
+ exmode = 0;
+ le_value = 0;
+ }
+ rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
+ outdata, outdatalen);
+ if (!rc && app->force_chv1)
+ app->did_chv1 = 0;
+
+ return rc;
+}
+
+/* Compute a digital signature using the INTERNAL AUTHENTICATE command
+ on INDATA which is expected to be the raw message digest. For this
+ application the KEYIDSTR consists of the serialnumber and the
+ fingerprint delimited by a slash. Optionally the id OPENPGP.3 may
+ be given.
+
+ Note that this function may return the error code
+ GPG_ERR_WRONG_CARD to indicate that the card currently present does
+ not match the one required for the requested action (e.g. the
+ serial number does not match). */
+static gpg_error_t
+do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ int rc;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+ && indatalen > 101) /* For a 2048 bit key. */
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (app->app_local->keyattr[2].key_type == KEY_TYPE_ECC)
+ {
+ if (!(app->app_local->keyattr[2].ecc.flags & ECC_FLAG_DJB_TWEAK)
+ && (indatalen == 51 || indatalen == 67 || indatalen == 83))
+ {
+ const char *p = (const char *)indata + 19;
+ indata = p;
+ indatalen -= 19;
+ }
+ else
+ {
+ const char *p = (const char *)indata + 15;
+ indata = p;
+ indatalen -= 15;
+ }
+ }
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (!ascii_strcasecmp (keyidstr, "OPENPGP.3"))
+ ;
+ else
+ {
+ rc = check_keyidstr (app, keyidstr, 3, NULL);
+ if (rc)
+ return rc;
+ }
+
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ if (!rc)
+ {
+ int exmode, le_value;
+
+ if (app->app_local->cardcap.ext_lc_le
+ && app->app_local->keyattr[2].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[2].rsa.n_bits > RSA_SMALL_SIZE_OP)
+ {
+ exmode = 1; /* Use extended length. */
+ le_value = app->app_local->keyattr[2].rsa.n_bits / 8;
+ }
+ else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+ {
+ exmode = -254; /* Command chaining with max. 254 bytes. */
+ le_value = 0;
+ }
+ else if (indatalen > 255)
+ {
+ if (!app->app_local->cardcap.ext_lc_le)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ exmode = 1;
+ le_value = 0;
+ }
+ else
+ {
+ exmode = 0;
+ le_value = 0;
+ }
+ rc = iso7816_internal_authenticate (app->slot, exmode,
+ indata, indatalen, le_value,
+ outdata, outdatalen);
+ }
+ return rc;
+}
+
+
+static gpg_error_t
+do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info)
+{
+ int rc;
+ int n;
+ int exmode, le_value;
+ unsigned char *fixbuf = NULL;
+ int padind = 0;
+ int fixuplen = 0;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr || !indatalen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (!ascii_strcasecmp (keyidstr, "OPENPGP.2"))
+ ;
+ else
+ {
+ rc = check_keyidstr (app, keyidstr, 2, NULL);
+ if (rc)
+ return rc;
+ }
+
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ if (rc)
+ return rc;
+
+ if ((indatalen == 16 + 1 || indatalen == 32 + 1)
+ && ((char *)indata)[0] == 0x02)
+ {
+ /* PSO:DECIPHER with symmetric key. */
+ padind = -1;
+ }
+ else if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
+ {
+ /* We might encounter a couple of leading zeroes in the
+ cryptogram. Due to internal use of MPIs these leading zeroes
+ are stripped. However the OpenPGP card expects exactly 128
+ bytes for the cryptogram (for a 1k key). Thus we need to fix
+ it up. We do this for up to 16 leading zero bytes; a
+ cryptogram with more than this is with a very high
+ probability anyway broken. If a signed conversion was used
+ we may also encounter one leading zero followed by the correct
+ length. We fix that as well. */
+ if (indatalen >= (128-16) && indatalen < 128) /* 1024 bit key. */
+ fixuplen = 128 - indatalen;
+ else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key. */
+ fixuplen = 192 - indatalen;
+ else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key. */
+ fixuplen = 256 - indatalen;
+ else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key. */
+ fixuplen = 384 - indatalen;
+ else if (indatalen >= (512-16) && indatalen < 512) /* 4096 bit key. */
+ fixuplen = 512 - indatalen;
+ else if (!*(const char *)indata && (indatalen == 129
+ || indatalen == 193
+ || indatalen == 257
+ || indatalen == 385
+ || indatalen == 513))
+ fixuplen = -1;
+ else
+ fixuplen = 0;
+
+ if (fixuplen > 0)
+ {
+ /* While we have to prepend stuff anyway, we can also
+ include the padding byte here so that iso1816_decipher
+ does not need to do another data mangling. */
+ fixuplen++;
+
+ fixbuf = xtrymalloc (fixuplen + indatalen);
+ if (!fixbuf)
+ return gpg_error_from_syserror ();
+
+ memset (fixbuf, 0, fixuplen);
+ memcpy (fixbuf+fixuplen, indata, indatalen);
+ indata = fixbuf;
+ indatalen = fixuplen + indatalen;
+ padind = -1; /* Already padded. */
+ }
+ else if (fixuplen < 0)
+ {
+ /* We use the extra leading zero as the padding byte. */
+ padind = -1;
+ }
+ }
+ else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+ {
+ int old_format_len = 0;
+
+ if ((app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK))
+ {
+ if (indatalen > 32 && (indatalen % 2))
+ { /*
+ * Skip the prefix. It may be 0x40 (in new format), or MPI
+ * head of 0x00 (in old format).
+ */
+ indata = (const char *)indata + 1;
+ indatalen--;
+ }
+ else if (indatalen < 32)
+ { /*
+ * Old format trancated by MPI handling.
+ */
+ old_format_len = indatalen;
+ indatalen = 32;
+ }
+ }
+
+ n = 0;
+ if (indatalen < 128)
+ fixuplen = 7;
+ else
+ fixuplen = 10;
+
+ fixbuf = xtrymalloc (fixuplen + indatalen);
+ if (!fixbuf)
+ return gpg_error_from_syserror ();
+
+ /* Build 'Cipher DO' */
+ fixbuf[n++] = '\xa6';
+ if (indatalen < 128)
+ fixbuf[n++] = (char)(indatalen+5);
+ else
+ {
+ fixbuf[n++] = 0x81;
+ fixbuf[n++] = (char)(indatalen+7);
+ }
+ fixbuf[n++] = '\x7f';
+ fixbuf[n++] = '\x49';
+ if (indatalen < 128)
+ fixbuf[n++] = (char)(indatalen+2);
+ else
+ {
+ fixbuf[n++] = 0x81;
+ fixbuf[n++] = (char)(indatalen+3);
+ }
+ fixbuf[n++] = '\x86';
+ if (indatalen < 128)
+ fixbuf[n++] = (char)indatalen;
+ else
+ {
+ fixbuf[n++] = 0x81;
+ fixbuf[n++] = (char)indatalen;
+ }
+
+ if (old_format_len)
+ {
+ memset (fixbuf+fixuplen, 0, 32 - old_format_len);
+ memcpy (fixbuf+fixuplen + 32 - old_format_len,
+ indata, old_format_len);
+ }
+ else
+ {
+ memcpy (fixbuf+fixuplen, indata, indatalen);
+ }
+ indata = fixbuf;
+ indatalen = fixuplen + indatalen;
+
+ padind = -1;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (app->app_local->cardcap.ext_lc_le
+ && (indatalen > 254
+ || (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA
+ && app->app_local->keyattr[1].rsa.n_bits > RSA_SMALL_SIZE_OP)))
+ {
+ exmode = 1; /* Extended length w/o a limit. */
+ le_value = app->app_local->keyattr[1].rsa.n_bits / 8;
+ }
+ else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+ {
+ exmode = -254; /* Command chaining with max. 254 bytes. */
+ le_value = 0;
+ }
+ else
+ exmode = le_value = 0;
+
+ rc = iso7816_decipher (app->slot, exmode,
+ indata, indatalen, le_value, padind,
+ outdata, outdatalen);
+ xfree (fixbuf);
+ if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
+ {
+ unsigned char prefix = 0;
+
+ if (app->app_local->keyattr[1].ecc.flags & ECC_FLAG_DJB_TWEAK)
+ prefix = 0x40;
+ else if ((*outdatalen % 2) == 0) /* No 0x04 -> x-coordinate only */
+ prefix = 0x41;
+
+ if (prefix)
+ { /* Add the prefix */
+ fixbuf = xtrymalloc (*outdatalen + 1);
+ if (!fixbuf)
+ {
+ xfree (*outdata);
+ return gpg_error_from_syserror ();
+ }
+ fixbuf[0] = prefix;
+ memcpy (fixbuf+1, *outdata, *outdatalen);
+ xfree (*outdata);
+ *outdata = fixbuf;
+ *outdatalen = *outdatalen + 1;
+ }
+ }
+
+ if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
+ && app->app_local->manufacturer == 5
+ && app->appversion == 0x0200)
+ log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
+ " do not work with encryption keys > 2048 bits\n");
+
+ *r_info |= APP_DECIPHER_INFO_NOPAD;
+
+ return rc;
+}
+
+
+/* Perform a simple verify operation for CHV1 and CHV2, so that
+ further operations won't ask for CHV2 and it is possible to do a
+ cheap check on the PIN: If there is something wrong with the PIN
+ entry system, only the regular CHV will get blocked and not the
+ dangerous CHV3. KEYIDSTR is the usual card's serial number; an
+ optional fingerprint part will be ignored.
+
+ There is a special mode if the keyidstr is "<serialno>[CHV3]" with
+ the "[CHV3]" being a literal string: The Admin Pin is checked if
+ and only if the retry counter is still at 3. */
+static gpg_error_t
+do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+ int admin_pin = 0;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ rc = check_keyidstr (app, keyidstr, 0, NULL);
+ if (rc)
+ return rc;
+
+ if ((strlen (keyidstr) >= 32+6 && !strcmp (keyidstr+32, "[CHV3]"))
+ || (strlen (keyidstr) >= 40+6 && !strcmp (keyidstr+40, "[CHV3]")))
+ admin_pin = 1;
+
+ /* Yes, there is a race conditions: The user might pull the card
+ right here and we won't notice that. However this is not a
+ problem and the check above is merely for a graceful failure
+ between operations. */
+
+ if (admin_pin)
+ {
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int count;
+
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ count = value[6];
+ xfree (relptr);
+
+ if (!count)
+ {
+ log_info (_("card is permanently locked!\n"));
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else if (count < 3)
+ {
+ log_info (_("verification of Admin PIN is currently prohibited "
+ "through this command\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ app->did_chv3 = 0; /* Force verification. */
+ return verify_chv3 (app, pincb, pincb_arg);
+ }
+ else
+ return verify_chv2 (app, pincb, pincb_arg);
+}
+
+
+/* Show information about card capabilities. */
+static void
+show_caps (struct app_local_s *s)
+{
+ log_info ("Version-2+ .....: %s\n", s->extcap.is_v2? "yes":"no");
+ log_info ("Extcap-v3 ......: %s\n", s->extcap.extcap_v3? "yes":"no");
+ log_info ("Button .........: %s\n", s->extcap.has_button? "yes":"no");
+
+ log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
+ if (s->extcap.sm_supported)
+ log_printf (" (%s)", s->extcap.sm_algo==2? "3DES":
+ (s->extcap.sm_algo==2? "AES-128" : "AES-256"));
+ log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
+ if (s->extcap.get_challenge)
+ log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
+ log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no");
+ log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
+ log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
+ log_info ("Algo-Attr-Change: %s\n", s->extcap.algo_attr_change? "yes":"no");
+ log_info ("Symmetric Crypto: %s\n", s->extcap.has_decrypt? "yes":"no");
+ log_info ("KDF-Support ....: %s\n", s->extcap.kdf_do? "yes":"no");
+ log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
+ if (s->extcap.extcap_v3)
+ {
+ log_info ("PIN-Block-2 ....: %s\n", s->extcap.pin_blk2? "yes":"no");
+ log_info ("MSE-Support ....: %s\n", s->extcap.mse? "yes":"no");
+ log_info ("Max-Special-DOs : %u\n", s->extcap.max_special_do);
+ }
+ log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
+ log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
+ log_info ("Status-Indicator: %02X\n", s->status_indicator);
+
+ log_info ("GnuPG-No-Sync ..: %s\n", s->flags.no_sync? "yes":"no");
+ log_info ("GnuPG-Def-PW2 ..: %s\n", s->flags.def_chv2? "yes":"no");
+}
+
+
+/* Parse the historical bytes in BUFFER of BUFLEN and store them in
+ APPLOC. */
+static void
+parse_historical (struct app_local_s *apploc,
+ const unsigned char * buffer, size_t buflen)
+{
+ /* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */
+ if (buflen < 4)
+ {
+ log_error ("warning: historical bytes are too short\n");
+ return; /* Too short. */
+ }
+ if (*buffer)
+ {
+ log_error ("warning: bad category indicator in historical bytes\n");
+ return;
+ }
+
+ /* Skip category indicator. */
+ buffer++;
+ buflen--;
+
+ /* Get the status indicator. */
+ apploc->status_indicator = buffer[buflen-3];
+ buflen -= 3;
+
+ /* Parse the compact TLV. */
+ while (buflen)
+ {
+ unsigned int tag = (*buffer & 0xf0) >> 4;
+ unsigned int len = (*buffer & 0x0f);
+ if (len+1 > buflen)
+ {
+ log_error ("warning: bad Compact-TLV in historical bytes\n");
+ return; /* Error. */
+ }
+ buffer++;
+ buflen--;
+ if (tag == 7 && len == 3)
+ {
+ /* Card capabilities. */
+ apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80);
+ apploc->cardcap.ext_lc_le = !!(buffer[2] & 0x40);
+ }
+ buffer += len;
+ buflen -= len;
+ }
+}
+
+
+/*
+ * Check if the OID in an DER encoding is available by GnuPG/libgcrypt,
+ * and return the curve name. Return NULL if not available.
+ * The constant string is not allocated dynamically, never free it.
+ */
+static const char *
+ecc_curve (unsigned char *buf, size_t buflen)
+{
+ gcry_mpi_t oid;
+ char *oidstr;
+ const char *result;
+ unsigned char *oidbuf;
+
+ oidbuf = xtrymalloc (buflen + 1);
+ if (!oidbuf)
+ return NULL;
+
+ memcpy (oidbuf+1, buf, buflen);
+ oidbuf[0] = buflen;
+ oid = gcry_mpi_set_opaque (NULL, oidbuf, (buflen+1) * 8);
+ if (!oid)
+ {
+ xfree (oidbuf);
+ return NULL;
+ }
+
+ oidstr = openpgp_oid_to_str (oid);
+ gcry_mpi_release (oid);
+ if (!oidstr)
+ return NULL;
+
+ result = openpgp_oid_to_curve (oidstr, 1);
+ xfree (oidstr);
+ return result;
+}
+
+
+/* Parse and optionally show the algorithm attributes for KEYNO.
+ KEYNO must be in the range 0..2. */
+static gpg_error_t
+parse_algorithm_attribute (app_t app, int keyno)
+{
+ unsigned char *buffer;
+ size_t buflen;
+ void *relptr;
+ const char desc[3][5] = {"sign", "encr", "auth"};
+ gpg_error_t err = 0;
+
+ assert (keyno >=0 && keyno <= 2);
+
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_RSA;
+ app->app_local->keyattr[keyno].rsa.n_bits = 0;
+
+ relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
+ if (!relptr)
+ {
+ log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (buflen < 1)
+ {
+ log_error ("error reading DO 0x%02X\n", 0xc1+keyno);
+ xfree (relptr);
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ if (opt.verbose)
+ log_info ("Key-Attr-%s ..: ", desc[keyno]);
+ if (*buffer == PUBKEY_ALGO_RSA && (buflen == 5 || buflen == 6))
+ {
+ app->app_local->keyattr[keyno].rsa.n_bits = (buffer[1]<<8 | buffer[2]);
+ app->app_local->keyattr[keyno].rsa.e_bits = (buffer[3]<<8 | buffer[4]);
+ app->app_local->keyattr[keyno].rsa.format = 0;
+ if (buflen < 6)
+ app->app_local->keyattr[keyno].rsa.format = RSA_STD;
+ else
+ app->app_local->keyattr[keyno].rsa.format = (buffer[5] == 0? RSA_STD :
+ buffer[5] == 1? RSA_STD_N :
+ buffer[5] == 2? RSA_CRT :
+ buffer[5] == 3? RSA_CRT_N :
+ RSA_UNKNOWN_FMT);
+
+ if (opt.verbose)
+ log_printf
+ ("RSA, n=%u, e=%u, fmt=%s\n",
+ app->app_local->keyattr[keyno].rsa.n_bits,
+ app->app_local->keyattr[keyno].rsa.e_bits,
+ app->app_local->keyattr[keyno].rsa.format == RSA_STD? "std" :
+ app->app_local->keyattr[keyno].rsa.format == RSA_STD_N?"std+n":
+ app->app_local->keyattr[keyno].rsa.format == RSA_CRT? "crt" :
+ app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N?"crt+n":"?");
+ }
+ else if (*buffer == PUBKEY_ALGO_ECDH || *buffer == PUBKEY_ALGO_ECDSA
+ || *buffer == PUBKEY_ALGO_EDDSA)
+ {
+ const char *curve;
+ int oidlen = buflen - 1;
+
+ app->app_local->keyattr[keyno].ecc.flags = 0;
+
+ if (APP_CARD(app)->cardtype == CARDTYPE_YUBIKEY)
+ {
+ /* Yubikey implementations vary.
+ * Firmware version 5.2 returns "pubkey required"-byte with
+ * 0x00, but after removal and second time insertion, it
+ * returns bogus value there.
+ * Firmware version 5.4 returns none.
+ */
+ curve = ecc_curve (buffer + 1, oidlen);
+ if (!curve)
+ curve = ecc_curve (buffer + 1, oidlen - 1);
+ }
+ else
+ {
+ if (buffer[buflen-1] == 0x00 || buffer[buflen-1] == 0xff)
+ { /* Found "pubkey required"-byte for private key template. */
+ oidlen--;
+ if (buffer[buflen-1] == 0xff)
+ app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_PUBKEY;
+ }
+ curve = ecc_curve (buffer + 1, oidlen);
+ }
+
+ if (!curve)
+ {
+ log_printhex (buffer+1, buflen-1, "Curve with OID not supported: ");
+ err = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ app->app_local->keyattr[keyno].key_type = KEY_TYPE_ECC;
+ app->app_local->keyattr[keyno].ecc.curve = curve;
+ if (*buffer == PUBKEY_ALGO_EDDSA
+ || (*buffer == PUBKEY_ALGO_ECDH
+ && !strcmp (app->app_local->keyattr[keyno].ecc.curve,
+ "Curve25519")))
+ app->app_local->keyattr[keyno].ecc.flags |= ECC_FLAG_DJB_TWEAK;
+ if (opt.verbose)
+ log_printf
+ ("ECC, curve=%s%s\n", app->app_local->keyattr[keyno].ecc.curve,
+ !(app->app_local->keyattr[keyno].ecc.flags & ECC_FLAG_DJB_TWEAK)?
+ "": keyno==1? " (djb-tweak)": " (eddsa)");
+ }
+ }
+ else if (opt.verbose)
+ log_printhex (buffer, buflen, "");
+
+ xfree (relptr);
+ return err;
+}
+
+/* Select the OpenPGP application on the card in SLOT. This function
+ must be used before any other OpenPGP application functions. */
+gpg_error_t
+app_select_openpgp (app_t app)
+{
+ static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
+ int slot = app->slot;
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ void *relptr;
+
+ /* Note that the card can't cope with P2=0xCO, thus we need to pass a
+ special flag value. */
+ err = iso7816_select_application (slot, aid, sizeof aid, 0x0001);
+ if (!err)
+ {
+ unsigned int manufacturer;
+
+ app->apptype = APPTYPE_OPENPGP;
+
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ app->did_chv3 = 0;
+ app->app_local = NULL;
+
+ /* The OpenPGP card returns the serial number as part of the
+ AID; because we prefer to use OpenPGP serial numbers, we
+ replace a possibly already set one from a EF.GDO with this
+ one. Note, that for current OpenPGP cards, no EF.GDO exists
+ and thus it won't matter at all. */
+ err = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen);
+ if (err)
+ goto leave;
+ if (opt.verbose)
+ {
+ log_info ("AID: ");
+ log_printhex (buffer, buflen, "");
+ }
+
+ app->appversion = buffer[6] << 8;
+ app->appversion |= buffer[7];
+ manufacturer = (buffer[8]<<8 | buffer[9]);
+
+ xfree (app->serialno);
+ app->serialno = buffer;
+ app->serialnolen = buflen;
+ buffer = NULL;
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ app->app_local->manufacturer = manufacturer;
+
+ if (app->appversion >= 0x0200)
+ app->app_local->extcap.is_v2 = 1;
+
+ if (app->appversion >= 0x0300)
+ app->app_local->extcap.extcap_v3 = 1;
+
+ /* Read the historical bytes. */
+ relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
+ if (relptr)
+ {
+ if (opt.verbose)
+ {
+ log_info ("Historical Bytes: ");
+ log_printhex (buffer, buflen, "");
+ }
+ parse_historical (app->app_local, buffer, buflen);
+ xfree (relptr);
+ }
+
+ /* Read the force-chv1 flag. */
+ relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
+ if (!relptr)
+ {
+ log_error (_("can't access %s - invalid OpenPGP card?\n"),
+ "CHV Status Bytes");
+ err = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ app->force_chv1 = (buflen && *buffer == 0);
+ xfree (relptr);
+
+ /* Read the extended capabilities. */
+ relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
+ if (!relptr)
+ {
+ log_error (_("can't access %s - invalid OpenPGP card?\n"),
+ "Extended Capability Flags" );
+ err = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ if (buflen)
+ {
+ app->app_local->extcap.sm_supported = !!(*buffer & 0x80);
+ app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
+ app->app_local->extcap.key_import = !!(*buffer & 0x20);
+ app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
+ app->app_local->extcap.private_dos = !!(*buffer & 0x08);
+ app->app_local->extcap.algo_attr_change = !!(*buffer & 0x04);
+ app->app_local->extcap.has_decrypt = !!(*buffer & 0x02);
+ app->app_local->extcap.kdf_do = !!(*buffer & 0x01);
+ }
+ if (buflen >= 10)
+ {
+ /* Available with cards of v2 or later. */
+ app->app_local->extcap.sm_algo = buffer[1];
+ app->app_local->extcap.max_get_challenge
+ = (buffer[2] << 8 | buffer[3]);
+ app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
+
+ /* Interpretation is different between v2 and v3, unfortunately. */
+ if (app->app_local->extcap.extcap_v3)
+ {
+ app->app_local->extcap.max_special_do
+ = (buffer[6] << 8 | buffer[7]);
+ app->app_local->extcap.pin_blk2 = !!(buffer[8] & 0x01);
+ app->app_local->extcap.mse= !!(buffer[9] & 0x01);
+ }
+ }
+ xfree (relptr);
+
+ /* Some of the first cards accidentally don't set the
+ CHANGE_FORCE_CHV bit but allow it anyway. */
+ if (app->appversion <= 0x0100 && manufacturer == 1)
+ app->app_local->extcap.change_force_chv = 1;
+
+ /* Check optional DO of "General Feature Management" for button. */
+ relptr = get_one_do (app, 0x7f74, &buffer, &buflen, NULL);
+ if (relptr)
+ /* It must be: 03 81 01 20 */
+ app->app_local->extcap.has_button = 1;
+
+ parse_login_data (app);
+
+ if (opt.verbose)
+ show_caps (app->app_local);
+
+ err = parse_algorithm_attribute (app, 0);
+ if (!err)
+ err = parse_algorithm_attribute (app, 1);
+ if (!err)
+ err = parse_algorithm_attribute (app, 2);
+ if (err)
+ goto leave;
+
+ if (opt.verbose > 1)
+ dump_all_do (slot);
+
+ app->fnc.deinit = do_deinit;
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = do_readcert;
+ app->fnc.readkey = do_readkey;
+ app->fnc.getattr = do_getattr;
+ app->fnc.setattr = do_setattr;
+ app->fnc.writecert = do_writecert;
+ app->fnc.writekey = do_writekey;
+ app->fnc.genkey = do_genkey;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = do_auth;
+ app->fnc.decipher = do_decipher;
+ app->fnc.change_pin = do_change_pin;
+ app->fnc.check_pin = do_check_pin;
+ }
+
+leave:
+ if (err)
+ do_deinit (app);
+ return err;
+}
diff --git a/scd/app-p15.c b/scd/app-p15.c
new file mode 100644
index 0000000..2884e0d
--- /dev/null
+++ b/scd/app-p15.c
@@ -0,0 +1,6290 @@
+/* app-p15.c - The pkcs#15 card application.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2021 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+/* Information pertaining to the BELPIC developer card samples:
+
+ Unblock PUK: "222222111111"
+ Reset PIN: "333333111111")
+
+ e.g. the APDUs 00:20:00:02:08:2C:33:33:33:11:11:11:FF
+ and 00:24:01:01:08:24:12:34:FF:FF:FF:FF:FF
+ should change the PIN into 1234.
+*/
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "scdaemon.h"
+
+#include "iso7816.h"
+#include "../common/i18n.h"
+#include "../common/tlv.h"
+#include "../common/host2net.h"
+#include "../common/openpgpdefs.h"
+#include "apdu.h" /* fixme: we should move the card detection to a
+ separate file */
+
+
+static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3";
+static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8";
+static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9";
+static const char oid_kp_ms_documentSigning[] = "1.3.6.1.4.1.311.10.3.12";
+static const char oid_kp_ms_old_documentSigning[] = "1.3.6.1.4.1.311.3.10.3.12";
+
+static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4";
+
+static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1";
+static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2";
+static const char oid_kp_ms_smartcardLogon[] = "1.3.6.1.4.1.311.20.2.2";
+
+static const char oid_kp_anyExtendedKeyUsage[] = "2.5.29.37.0";
+
+static const char oid_kp_gpgUsageCert[] = "1.3.6.1.4.1.11591.2.6.1";
+static const char oid_kp_gpgUsageSign[] = "1.3.6.1.4.1.11591.2.6.2";
+static const char oid_kp_gpgUsageEncr[] = "1.3.6.1.4.1.11591.2.6.3";
+static const char oid_kp_gpgUsageAuth[] = "1.3.6.1.4.1.11591.2.6.4";
+
+/* Types of cards we know and which needs special treatment. */
+typedef enum
+ {
+ CARD_TYPE_UNKNOWN,
+ CARD_TYPE_TCOS,
+ CARD_TYPE_MICARDO,
+ CARD_TYPE_CARDOS_50,
+ CARD_TYPE_CARDOS_53,
+ CARD_TYPE_AET, /* A.E.T. Europe JCOP card. */
+ CARD_TYPE_BELPIC /* Belgian eID card specs. */
+ }
+card_type_t;
+
+/* The OS of card as specified by card_type_t is not always
+ * sufficient. Thus we also distinguish the actual product build upon
+ * the given OS. */
+typedef enum
+ {
+ CARD_PRODUCT_UNKNOWN,
+ CARD_PRODUCT_RSCS, /* Rohde&Schwarz Cybersecurity */
+ CARD_PRODUCT_DTRUST, /* D-Trust GmbH (bundesdruckerei.de) */
+ CARD_PRODUCT_GENUA /* GeNUA mbH */
+ }
+card_product_t;
+
+
+/* A list card types with ATRs noticed with these cards. */
+#define X(a) ((unsigned char const *)(a))
+static struct
+{
+ size_t atrlen;
+ unsigned char const *atr;
+ card_type_t type;
+} card_atr_list[] = {
+ { 19, X("\x3B\xBA\x13\x00\x81\x31\x86\x5D\x00\x64\x05\x0A\x02\x01\x31\x80"
+ "\x90\x00\x8B"),
+ CARD_TYPE_TCOS }, /* SLE44 */
+ { 19, X("\x3B\xBA\x14\x00\x81\x31\x86\x5D\x00\x64\x05\x14\x02\x02\x31\x80"
+ "\x90\x00\x91"),
+ CARD_TYPE_TCOS }, /* SLE66S */
+ { 19, X("\x3B\xBA\x96\x00\x81\x31\x86\x5D\x00\x64\x05\x60\x02\x03\x31\x80"
+ "\x90\x00\x66"),
+ CARD_TYPE_TCOS }, /* SLE66P */
+ { 27, X("\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00"
+ "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23"),
+ CARD_TYPE_MICARDO }, /* German BMI card */
+ { 19, X("\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80"
+ "\x00\x90\x00"),
+ CARD_TYPE_MICARDO }, /* German BMI card (ATR due to reader problem) */
+ { 26, X("\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49"
+ "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43"),
+ CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */
+ { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x01\x14"),
+ CARD_TYPE_CARDOS_50 }, /* CardOS 5.0 */
+ { 11, X("\x3b\xd2\x18\x00\x81\x31\xfe\x58\xc9\x03\x16"),
+ CARD_TYPE_CARDOS_53 }, /* CardOS 5.3 */
+ { 24, X("\x3b\xfe\x18\x00\x00\x80\x31\xfe\x45\x53\x43\x45"
+ "\x36\x30\x2d\x43\x44\x30\x38\x31\x2d\x6e\x46\xa9"),
+ CARD_TYPE_AET },
+ { 0 }
+};
+#undef X
+
+
+/* Macro to test for CardOS 5.0 and 5.3. */
+#define IS_CARDOS_5(a) ((a)->app_local->card_type == CARD_TYPE_CARDOS_50 \
+ || (a)->app_local->card_type == CARD_TYPE_CARDOS_53)
+
+/* The default PKCS-15 home DF */
+#define DEFAULT_HOME_DF 0x5015
+
+/* The AID of PKCS15. */
+static char const pkcs15_aid[] = { 0xA0, 0, 0, 0, 0x63,
+ 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+/* The Belgian eID variant - they didn't understood why a shared AID
+ is useful for a standard. Oh well. */
+static char const pkcs15be_aid[] = { 0xA0, 0, 0, 0x01, 0x77,
+ 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
+
+
+/* The PIN types as defined in pkcs#15 v1.1 */
+typedef enum
+ {
+ PIN_TYPE_BCD = 0,
+ PIN_TYPE_ASCII_NUMERIC = 1,
+ PIN_TYPE_UTF8 = 2,
+ PIN_TYPE_HALF_NIBBLE_BCD = 3,
+ PIN_TYPE_ISO9564_1 = 4
+ } pin_type_t;
+
+/* The AuthenticationTypes as defined in pkcs#15 v1.1 (6.8.1) */
+typedef enum
+ {
+ AUTH_TYPE_PIN = -1,
+ AUTH_TYPE_BIOMETRIC = 0,
+ AUTH_TYPE_AUTHKEY = 1,
+ AUTH_TYPE_EXTERNAL = 2,
+ } auth_type_t;
+
+/* A bit array with for the key usage flags from the
+ commonKeyAttributes. */
+struct keyusage_flags_s
+{
+ unsigned int encrypt: 1;
+ unsigned int decrypt: 1;
+ unsigned int sign: 1;
+ unsigned int sign_recover: 1;
+ unsigned int wrap: 1;
+ unsigned int unwrap: 1;
+ unsigned int verify: 1;
+ unsigned int verify_recover: 1;
+ unsigned int derive: 1;
+ unsigned int non_repudiation: 1;
+};
+typedef struct keyusage_flags_s keyusage_flags_t;
+
+
+/* A bit array with for the key access flags from the
+ commonKeyAttributes. */
+struct keyaccess_flags_s
+{
+ unsigned int any:1; /* Any access flag set. */
+ unsigned int sensitive:1;
+ unsigned int extractable:1;
+ unsigned int always_sensitive:1;
+ unsigned int never_extractable:1;
+ unsigned int local:1;
+};
+typedef struct keyaccess_flags_s keyaccess_flags_t;
+
+
+/* A bit array with for the gpg usage flags. */
+struct gpgusage_flags_s
+{
+ unsigned int any:1; /* Any of the next flags are set. */
+ unsigned int cert:1; /* 1.3.6.1.4.1.11591.2.6.1 */
+ unsigned int sign:1; /* 1.3.6.1.4.1.11591.2.6.2 */
+ unsigned int encr:1; /* 1.3.6.1.4.1.11591.2.6.3 */
+ unsigned int auth:1; /* 1.3.6.1.4.1.11591.2.6.4 */
+};
+typedef struct gpgusage_flags_s gpgusage_flags_t;
+
+
+/* This is an object to store information about a Certificate
+ Directory File (CDF) in a format suitable for further processing by
+ us. To keep memory management, simple we use a linked list of
+ items; i.e. one such object represents one certificate and the list
+ the entire CDF. */
+struct cdf_object_s
+{
+ /* Link to next item when used in a linked list. */
+ struct cdf_object_s *next;
+
+ /* Flags to indicate whether fields are valid. */
+ unsigned int have_off:1;
+
+ /* Length and allocated buffer with the Id of this object.
+ * This field is used for X.509 in PKCS#11 to make it easier to
+ * match a private key with a certificate. */
+ size_t objidlen;
+ unsigned char *objid;
+
+ /* Length and allocated buffer with the authId of this object or
+ NULL if no authID is known. */
+ size_t authidlen;
+ unsigned char *authid;
+
+ /* NULL or the malloced label of this object. */
+ char *label;
+
+ /* To avoid reading and parsing a certificate more than once, we
+ * cache the ksba object. */
+ ksba_cert_t cert;
+
+ /* The offset and length of the object. They are only valid if
+ HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
+ unsigned long off, len;
+
+ /* The length of the path as given in the CDF and the path itself.
+ path[0] is the top DF (usually 0x3f00). The path will never be
+ empty. */
+ size_t pathlen;
+ unsigned short path[1];
+};
+typedef struct cdf_object_s *cdf_object_t;
+
+
+/* This is an object to store information about a Private Key
+ Directory File (PrKDF) in a format suitable for further processing
+ by us. To keep memory management, simple we use a linked list of
+ items; i.e. one such object represents one certificate and the list
+ the entire PrKDF. */
+struct prkdf_object_s
+{
+ /* Link to next item when used in a linked list. */
+ struct prkdf_object_s *next;
+
+ /* Flags to indicate whether fields are valid. */
+ unsigned int keygrip_valid:1;
+ unsigned int key_reference_valid:1;
+ unsigned int have_off:1;
+ unsigned int have_keytime:1;
+
+ /* Flag indicating that the corresponding PIN has already been
+ * verified. Note that for cards which are able to return the
+ * verification stus, this flag is not used. */
+ unsigned int pin_verified:1;
+
+ /* PKCS#15 info whether this is an EC key. Default is RSA. Note
+ * that there is also a KEYALGO field which is derived from the
+ * publick key via Libgcrypt. */
+ unsigned int is_ecc:1;
+
+ /* The key's usage flags. */
+ keyusage_flags_t usageflags;
+
+ /* The key's access flags. */
+ keyaccess_flags_t accessflags;
+
+ /* Extended key usage flags. Only used if .valid is set. This
+ * information is computed from an associated certificate15. */
+ struct {
+ unsigned int valid:1;
+ unsigned int sign:1;
+ unsigned int encr:1;
+ unsigned int auth:1;
+ } extusage;
+
+ /* OpenPGP key features for this key. This is taken from special
+ * extended key usage flags different from those tracked in EXTUSAGE
+ * above. There is also no valid flag as in EXTUSAGE. */
+ gpgusage_flags_t gpgusage;
+
+ /* The keygrip of the key. This is used as a cache. */
+ char keygrip[2*KEYGRIP_LEN+1];
+
+ /* A malloced algorithm string or NULL if not known. */
+ char *keyalgostr;
+
+ /* The Gcrypt algo identifier for the key. It is valid if the
+ * keygrip is also valid. See also is_ecc above. */
+ int keyalgo;
+
+ /* The length of the key in bits (e.g. for RSA the length of the
+ * modulus). It is valid if the keygrip is also valid. */
+ unsigned int keynbits;
+
+ /* The creation time of the key or 0 if not known. */
+ u32 keytime;
+
+ /* Malloced CN from the Subject-DN of the corresponding certificate
+ * or NULL if not known. */
+ char *common_name;
+
+ /* Malloced SerialNumber from the Subject-DN of the corresponding
+ * certificate or NULL if not known. */
+ char *serial_number;
+
+ /* KDF/KEK parameter for OpenPGP's ECDH. First byte is zero if not
+ * availabale. .*/
+ unsigned char ecdh_kdf[4];
+
+ /* Length and allocated buffer with the Id of this object. */
+ size_t objidlen;
+ unsigned char *objid;
+
+ /* Length and allocated buffer with the authId of this object or
+ NULL if no authID is known. */
+ size_t authidlen;
+ unsigned char *authid;
+
+ /* NULL or the malloced label of this object. */
+ char *label;
+
+ /* The keyReference and a flag telling whether it is valid. */
+ unsigned long key_reference;
+
+ /* The offset and length of the object. They are only valid if
+ * HAVE_OFF is true otherwise they are set to 0. */
+ unsigned long off, len;
+
+ /* The length of the path as given in the PrKDF and the path itself.
+ path[0] is the top DF (usually 0x3f00). */
+ size_t pathlen;
+ unsigned short path[1];
+};
+typedef struct prkdf_object_s *prkdf_object_t;
+typedef struct prkdf_object_s *pukdf_object_t;
+
+
+/* This is an object to store information about a Authentication
+ Object Directory File (AODF) in a format suitable for further
+ processing by us. To keep memory management, simple we use a linked
+ list of items; i.e. one such object represents one authentication
+ object and the list the entire AOKDF. */
+struct aodf_object_s
+{
+ /* Link to next item when used in a linked list. */
+ struct aodf_object_s *next;
+
+ /* Flags to indicate whether fields are valid. */
+ unsigned int have_off:1;
+
+ /* Length and allocated buffer with the Id of this object. */
+ size_t objidlen;
+ unsigned char *objid;
+
+ /* Length and allocated buffer with the authId of this object or
+ NULL if no authID is known. */
+ size_t authidlen;
+ unsigned char *authid;
+
+ /* NULL or the malloced label of this object. */
+ char *label;
+
+ /* The file ID of this AODF. */
+ unsigned short fid;
+
+ /* The type of this authentication object. */
+ auth_type_t auth_type;
+
+ /* Info used for AUTH_TYPE_PIN: */
+
+ /* The PIN Flags. */
+ struct
+ {
+ unsigned int case_sensitive: 1;
+ unsigned int local: 1;
+ unsigned int change_disabled: 1;
+ unsigned int unblock_disabled: 1;
+ unsigned int initialized: 1;
+ unsigned int needs_padding: 1;
+ unsigned int unblocking_pin: 1;
+ unsigned int so_pin: 1;
+ unsigned int disable_allowed: 1;
+ unsigned int integrity_protected: 1;
+ unsigned int confidentiality_protected: 1;
+ unsigned int exchange_ref_data: 1;
+ } pinflags;
+
+ /* The PIN Type. */
+ pin_type_t pintype;
+
+ /* The minimum length of a PIN. */
+ unsigned long min_length;
+
+ /* The stored length of a PIN. */
+ unsigned long stored_length;
+
+ /* The maximum length of a PIN and a flag telling whether it is valid. */
+ unsigned long max_length;
+ int max_length_valid;
+
+ /* The pinReference and a flag telling whether it is valid. */
+ unsigned long pin_reference;
+ int pin_reference_valid;
+
+ /* The padChar and a flag telling whether it is valid. */
+ char pad_char;
+ int pad_char_valid;
+
+ /* The offset and length of the object. They are only valid if
+ HAVE_OFF is true and set to 0 if HAVE_OFF is false. */
+ unsigned long off, len;
+
+ /* The length of the path as given in the Aodf and the path itself.
+ path[0] is the top DF (usually 0x3f00). PATH is optional and thus
+ may be NULL. Malloced.*/
+ size_t pathlen;
+ unsigned short *path;
+
+ /* Info used for AUTH_TYPE_AUTHKEY: */
+
+};
+typedef struct aodf_object_s *aodf_object_t;
+
+
+/* Context local to this application. */
+struct app_local_s
+{
+ /* The home DF. Note, that we don't yet support a multilevel
+ hierarchy. Thus we assume this is directly below the MF. */
+ unsigned short home_df;
+
+ /* The type of the card's OS. */
+ card_type_t card_type;
+
+ /* The vendor's product. */
+ card_product_t card_product;
+
+ /* Flag indicating that extended_mode is not supported. */
+ unsigned int no_extended_mode : 1;
+
+ /* Flag indicating whether we may use direct path selection. */
+ unsigned int direct_path_selection : 1;
+
+ /* Flag indicating whether the card has any key with a gpgusage set. */
+ unsigned int any_gpgusage : 1;
+
+ /* Structure with the EFIDs of the objects described in the ODF
+ file. */
+ struct
+ {
+ unsigned short private_keys;
+ unsigned short public_keys;
+ unsigned short trusted_public_keys;
+ unsigned short secret_keys;
+ unsigned short certificates;
+ unsigned short trusted_certificates;
+ unsigned short useful_certificates;
+ unsigned short data_objects;
+ unsigned short auth_objects;
+ } odf;
+
+ /* The PKCS#15 serialnumber from EF(TokenInfo) or NULL. Malloced. */
+ unsigned char *serialno;
+ size_t serialnolen;
+
+ /* The manufacturerID from the TokenInfo EF. Malloced or NULL. */
+ char *manufacturer_id;
+
+ /* The label from the TokenInfo EF. Malloced or NULL. */
+ char *token_label;
+
+ /* The tokenflags from the TokenInfo EF. Malloced or NULL. */
+ unsigned char *tokenflags;
+ unsigned int tokenflagslen;
+
+ /* Information on all certificates. */
+ cdf_object_t certificate_info;
+ /* Information on all trusted certificates. */
+ cdf_object_t trusted_certificate_info;
+ /* Information on all useful certificates. */
+ cdf_object_t useful_certificate_info;
+
+ /* Information on all public keys. */
+ prkdf_object_t public_key_info;
+
+ /* Information on all private keys. */
+ pukdf_object_t private_key_info;
+
+ /* Information on all authentication objects. */
+ aodf_object_t auth_object_info;
+
+};
+
+
+/*** Local prototypes. ***/
+static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path,
+ size_t pathlen);
+static gpg_error_t keygrip_from_prkdf (app_t app, prkdf_object_t prkdf);
+static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
+ unsigned char **r_cert, size_t *r_certlen);
+static char *get_dispserialno (app_t app, prkdf_object_t prkdf);
+static gpg_error_t do_getattr (app_t app, ctrl_t ctrl, const char *name);
+
+
+
+static const char *
+cardtype2str (card_type_t cardtype)
+{
+ switch (cardtype)
+ {
+ case CARD_TYPE_UNKNOWN: return "";
+ case CARD_TYPE_TCOS: return "TCOS";
+ case CARD_TYPE_MICARDO: return "Micardo";
+ case CARD_TYPE_CARDOS_50: return "CardOS 5.0";
+ case CARD_TYPE_CARDOS_53: return "CardOS 5.3";
+ case CARD_TYPE_BELPIC: return "Belgian eID";
+ case CARD_TYPE_AET: return "AET";
+ }
+ return "";
+}
+
+static const char *
+cardproduct2str (card_product_t cardproduct)
+{
+ switch (cardproduct)
+ {
+ case CARD_PRODUCT_UNKNOWN: return "";
+ case CARD_PRODUCT_RSCS: return "R&S";
+ case CARD_PRODUCT_DTRUST: return "D-Trust";
+ case CARD_PRODUCT_GENUA: return "GeNUA";
+ }
+ return "";
+}
+
+/* Release the CDF object A */
+static void
+release_cdflist (cdf_object_t a)
+{
+ while (a)
+ {
+ cdf_object_t tmp = a->next;
+ ksba_free (a->cert);
+ xfree (a->objid);
+ xfree (a->authid);
+ xfree (a->label);
+ xfree (a);
+ a = tmp;
+ }
+}
+
+/* Release the PrKDF object A. */
+static void
+release_prkdflist (prkdf_object_t a)
+{
+ while (a)
+ {
+ prkdf_object_t tmp = a->next;
+ xfree (a->keyalgostr);
+ xfree (a->common_name);
+ xfree (a->serial_number);
+ xfree (a->objid);
+ xfree (a->authid);
+ xfree (a->label);
+ xfree (a);
+ a = tmp;
+ }
+}
+
+static void
+release_pukdflist (pukdf_object_t a)
+{
+ release_prkdflist (a);
+}
+
+/* Release just one aodf object. */
+void
+release_aodf_object (aodf_object_t a)
+{
+ if (a)
+ {
+ xfree (a->objid);
+ xfree (a->authid);
+ xfree (a->label);
+ xfree (a->path);
+ xfree (a);
+ }
+}
+
+/* Release the AODF list A. */
+static void
+release_aodflist (aodf_object_t a)
+{
+ while (a)
+ {
+ aodf_object_t tmp = a->next;
+ release_aodf_object (a);
+ a = tmp;
+ }
+}
+
+
+static void
+release_lists (app_t app)
+{
+ release_cdflist (app->app_local->certificate_info);
+ app->app_local->certificate_info = NULL;
+ release_cdflist (app->app_local->trusted_certificate_info);
+ app->app_local->trusted_certificate_info = NULL;
+ release_cdflist (app->app_local->useful_certificate_info);
+ app->app_local->useful_certificate_info = NULL;
+ release_pukdflist (app->app_local->public_key_info);
+ app->app_local->public_key_info = NULL;
+ release_prkdflist (app->app_local->private_key_info);
+ app->app_local->private_key_info = NULL;
+ release_aodflist (app->app_local->auth_object_info);
+ app->app_local->auth_object_info = NULL;
+}
+
+
+static void
+release_tokeninfo (app_t app)
+{
+ xfree (app->app_local->manufacturer_id);
+ app->app_local->manufacturer_id = NULL;
+ xfree (app->app_local->token_label);
+ app->app_local->token_label = NULL;
+ xfree (app->app_local->tokenflags);
+ app->app_local->tokenflags = NULL;
+ xfree (app->app_local->serialno);
+ app->app_local->serialno = NULL;
+}
+
+
+/* Release all local resources. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ release_lists (app);
+ release_tokeninfo (app);
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+/* Do a select and a read for the file with EFID. EFID_DESC is a
+ desctription of the EF to be used with error messages. On success
+ BUFFER and BUFLEN contain the entire content of the EF. The caller
+ must free BUFFER only on success. If EFID is 0 no seelct is done. */
+static gpg_error_t
+select_and_read_binary (app_t app, unsigned short efid, const char *efid_desc,
+ unsigned char **buffer, size_t *buflen)
+{
+ gpg_error_t err;
+ int sw;
+
+ if (efid)
+ {
+ err = select_ef_by_path (app, &efid, 1);
+ if (err)
+ {
+ log_error ("p15: error selecting %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
+ return err;
+ }
+ }
+
+ err = iso7816_read_binary_ext (app_get_slot (app),
+ 0, 0, 0, buffer, buflen, &sw);
+ if (err)
+ log_error ("p15: error reading %s (0x%04X): %s (sw=%04X)\n",
+ efid_desc, efid, gpg_strerror (err), sw);
+ return err;
+}
+
+
+/* If EFID is not 0 do a select and then read the record RECNO.
+ * EFID_DESC is a description of the EF to be used with error
+ * messages. On success BUFFER and BUFLEN contain the entire content
+ * of the EF. The caller must free BUFFER only on success. */
+static gpg_error_t
+select_and_read_record (app_t app, unsigned short efid, int recno,
+ const char *efid_desc,
+ unsigned char **buffer, size_t *buflen, int *r_sw)
+{
+ gpg_error_t err;
+ int sw;
+
+ if (r_sw)
+ *r_sw = 0x9000;
+
+ if (efid)
+ {
+ err = select_ef_by_path (app, &efid, 1);
+ if (err)
+ {
+ log_error ("p15: error selecting %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
+ if (r_sw)
+ *r_sw = sw;
+ return err;
+ }
+ }
+
+ err = iso7816_read_record_ext (app_get_slot (app),
+ recno, 1, 0, buffer, buflen, &sw);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ ;
+ else if (err && sw == SW_FILE_STRUCT)
+ ;
+ else
+ log_error ("p15: error reading %s (0x%04X) record %d: %s (sw=%04X)\n",
+ efid_desc, efid, recno, gpg_strerror (err), sw);
+ if (r_sw)
+ *r_sw = sw;
+ return err;
+ }
+ /* On CardOS with a Linear TLV file structure the records starts
+ * with some tag (often the record number) followed by the length
+ * byte for this record. Detect and remove this prefix. */
+ if (*buflen > 2 && (*buffer)[0] != 0x30 && (*buffer)[1] == *buflen - 2)
+ {
+ memmove (*buffer, *buffer + 2, *buflen - 2);
+ *buflen = *buflen - 2;
+ }
+
+ return 0;
+}
+
+
+/* This function calls select file to read a file using a complete
+ path which may or may not start at the master file (MF). */
+static gpg_error_t
+select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen)
+{
+ gpg_error_t err;
+ int i, j;
+
+ if (!pathlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* log_debug ("%s: path=", __func__); */
+ /* for (j=0; j < pathlen; j++) */
+ /* log_printf ("%s%04hX", j? "/":"", path[j]); */
+ /* log_printf ("%s\n",app->app_local->direct_path_selection?" (direct)":"");*/
+
+ if (app->app_local->direct_path_selection)
+ {
+ if (pathlen && *path == 0x3f00 )
+ {
+ if (pathlen == 1)
+ err = iso7816_select_mf (app_get_slot (app));
+ else
+ err = iso7816_select_path (app_get_slot (app), path+1, pathlen-1,
+ 0);
+ }
+ else
+ err = iso7816_select_path (app_get_slot (app), path, pathlen,
+ app->app_local->home_df);
+ if (err)
+ {
+ log_error ("p15: error selecting path ");
+ goto err_print_path;
+ }
+ }
+ else if (pathlen > 1 && path[0] == 0x3fff)
+ {
+ err = iso7816_select_file (app_get_slot (app), 0x3f00, 0);
+ if (err)
+ {
+ log_error ("p15: error selecting part %d from path ", 0);
+ goto err_print_path;
+ }
+ path++;
+ pathlen--;
+ for (i=0; i < pathlen; i++)
+ {
+ err = iso7816_select_file (app_get_slot (app),
+ path[i], (i+1 == pathlen)? 2 : 1);
+ if (err)
+ {
+ log_error ("p15: error selecting part %d from path ", i);
+ goto err_print_path;
+ }
+ }
+ }
+ else
+ {
+ if (pathlen && *path != 0x3f00 )
+ log_error ("p15: warning: relative path select not yet implemented\n");
+
+ /* FIXME: Use home_df. */
+ for (i=0; i < pathlen; i++)
+ {
+ err = iso7816_select_file (app_get_slot (app),
+ path[i], !(i+1 == pathlen));
+ if (err)
+ {
+ log_error ("p15: error selecting part %d from path ", i);
+ goto err_print_path;
+ }
+ }
+ }
+ return 0;
+
+ err_print_path:
+ if (pathlen && *path != 0x3f00 )
+ log_printf ("3F00/");
+ else
+ log_printf ("%04hX/", app->app_local->home_df);
+ for (j=0; j < pathlen; j++)
+ log_printf ("%s%04hX", j? "/":"", path[j]);
+ log_printf (": %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Parse a cert Id string (or a key Id string) and return the binary
+ object Id string in a newly allocated buffer stored at R_OBJID and
+ R_OBJIDLEN. On Error NULL will be stored there and an error code
+ returned. On success caller needs to free the buffer at R_OBJID. */
+static gpg_error_t
+parse_certid (app_t app, const char *certid,
+ unsigned char **r_objid, size_t *r_objidlen)
+{
+ char tmpbuf[10];
+ const char *s;
+ size_t objidlen;
+ unsigned char *objid;
+ int i;
+
+ *r_objid = NULL;
+ *r_objidlen = 0;
+
+ if (certid[0] != 'P' && strlen (certid) == 40) /* This is a keygrip. */
+ {
+ prkdf_object_t prkdf;
+
+ for (prkdf = app->app_local->private_key_info;
+ prkdf; prkdf = prkdf->next)
+ if (!keygrip_from_prkdf (app, prkdf)
+ && !strcmp (certid, prkdf->keygrip))
+ break;
+ if (!prkdf || !prkdf->objidlen || !prkdf->objid)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ objidlen = prkdf->objidlen;
+ objid = xtrymalloc (objidlen);
+ if (!objid)
+ return gpg_error_from_syserror ();
+ memcpy (objid, prkdf->objid, prkdf->objidlen);
+ }
+ else /* This is a usual keyref. */
+ {
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
+ snprintf (tmpbuf, sizeof tmpbuf, "P15-%04X.",
+ (unsigned int)(app->app_local->home_df & 0xffff));
+ else
+ strcpy (tmpbuf, "P15.");
+ if (strncmp (certid, tmpbuf, strlen (tmpbuf)) )
+ {
+ if (!strncmp (certid, "P15.", 4)
+ || (!strncmp (certid, "P15-", 4)
+ && hexdigitp (certid+4)
+ && hexdigitp (certid+5)
+ && hexdigitp (certid+6)
+ && hexdigitp (certid+7)
+ && certid[8] == '.'))
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ return gpg_error (GPG_ERR_INV_ID);
+ }
+ certid += strlen (tmpbuf);
+ for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
+ ;
+ if (*s || !objidlen || (objidlen%2))
+ return gpg_error (GPG_ERR_INV_ID);
+ objidlen /= 2;
+ objid = xtrymalloc (objidlen);
+ if (!objid)
+ return gpg_error_from_syserror ();
+ for (s=certid, i=0; i < objidlen; i++, s+=2)
+ objid[i] = xtoi_2 (s);
+ }
+
+ *r_objid = objid;
+ *r_objidlen = objidlen;
+ return 0;
+}
+
+
+/* Find a certificate object by its object ID and store a pointer to
+ * it at R_CDF. */
+static gpg_error_t
+cdf_object_from_objid (app_t app, size_t objidlen, const unsigned char *objid,
+ cdf_object_t *r_cdf)
+{
+ cdf_object_t cdf;
+
+ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
+ break;
+ if (!cdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_cdf = cdf;
+ return 0;
+}
+
+
+/* Find a certificate object by its label and store a pointer to it at
+ * R_CDF. */
+static gpg_error_t
+cdf_object_from_label (app_t app, const char *label, cdf_object_t *r_cdf)
+{
+ cdf_object_t cdf;
+
+ if (!label)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->useful_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->label && !strcmp (cdf->label, label))
+ break;
+ if (!cdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_cdf = cdf;
+ return 0;
+}
+
+
+/* Find a certificate object by the certificate ID CERTID and store a
+ * pointer to it at R_CDF. */
+static gpg_error_t
+cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf)
+{
+ gpg_error_t err;
+ size_t objidlen;
+ unsigned char *objid;
+ cdf_object_t cdf;
+ prkdf_object_t prkdf;
+
+ err = parse_certid (app, certid, &objid, &objidlen);
+ if (err)
+ return err;
+
+ err = cdf_object_from_objid (app, objidlen, objid, &cdf);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ {
+ /* Try again by finding the certid in the prkdf and matching by
+ * label. */
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ if (prkdf->objidlen == objidlen
+ && !memcmp (prkdf->objid, objid, objidlen))
+ break;
+ if (prkdf)
+ err = cdf_object_from_label (app, prkdf->label, &cdf);
+ }
+ xfree (objid);
+ if (err)
+ return err;
+ *r_cdf = cdf;
+ return 0;
+}
+
+
+/* Find a private key object by the key Id string KEYIDSTR and store a
+ pointer to it at R_PRKDF. */
+static gpg_error_t
+prkdf_object_from_keyidstr (app_t app, const char *keyidstr,
+ prkdf_object_t *r_prkdf)
+{
+ gpg_error_t err;
+ size_t objidlen;
+ unsigned char *objid;
+ prkdf_object_t prkdf;
+
+ err = parse_certid (app, keyidstr, &objid, &objidlen);
+ if (err)
+ return err;
+
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen))
+ break;
+ xfree (objid);
+ if (!prkdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_prkdf = prkdf;
+ return 0;
+}
+
+
+
+
+/* Read and parse the Object Directory File and store away the
+ pointers. ODF_FID shall contain the FID of the ODF.
+
+ Example of such a file:
+
+ A0 06 30 04 04 02 60 34 = Private Keys
+ A4 06 30 04 04 02 60 35 = Certificates
+ A5 06 30 04 04 02 60 36 = Trusted Certificates
+ A7 06 30 04 04 02 60 37 = Data Objects
+ A8 06 30 04 04 02 60 38 = Auth Objects
+
+ These are all PathOrObjects using the path CHOICE element. The
+ paths are octet strings of length 2. Using this Path CHOICE
+ element is recommended, so we only implement that for now.
+*/
+static gpg_error_t
+read_ef_odf (app_t app, unsigned short odf_fid)
+{
+ gpg_error_t err;
+ unsigned char *buffer, *p;
+ size_t buflen, n;
+ unsigned short value;
+ size_t offset;
+ unsigned short home_df = 0;
+
+
+ app->app_local->odf.private_keys = 0;
+ app->app_local->odf.public_keys = 0;
+ app->app_local->odf.trusted_public_keys = 0;
+ app->app_local->odf.secret_keys = 0;
+ app->app_local->odf.certificates = 0;
+ app->app_local->odf.trusted_certificates = 0;
+ app->app_local->odf.useful_certificates = 0;
+ app->app_local->odf.data_objects = 0;
+ app->app_local->odf.auth_objects = 0;
+
+ err = select_and_read_binary (app, odf_fid, "ODF",
+ &buffer, &buflen);
+ if (err)
+ return err;
+
+ if (buflen < 8)
+ {
+ log_error ("p15: error: ODF too short\n");
+ xfree (buffer);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+
+ home_df = app->app_local->home_df;
+ p = buffer;
+ while (buflen && *p && *p != 0xff)
+ {
+ if ( buflen >= 8
+ && (p[0] & 0xf0) == 0xA0
+ && !memcmp (p+1, "\x06\x30\x04\x04\x02", 5) )
+ {
+ offset = 6;
+ }
+ else if ( buflen >= 12
+ && (p[0] & 0xf0) == 0xA0
+ && !memcmp (p+1, "\x0a\x30\x08\x04\x06\x3F\x00", 7)
+ && (!home_df || home_df == ((p[8]<<8)|p[9])) )
+ {
+ /* FIXME: Is this hack still required? */
+ /* If we do not know the home DF, we take it from the first
+ * ODF object. Here are sample values:
+ * a0 0a 30 08 0406 3f00 5015 4401
+ * a1 0a 30 08 0406 3f00 5015 4411
+ * a4 0a 30 08 0406 3f00 5015 4441
+ * a5 0a 30 08 0406 3f00 5015 4451
+ * a8 0a 30 08 0406 3f00 5015 4481
+ * 00000000 */
+ if (!home_df)
+ {
+ home_df = ((p[8]<<8)|p[9]);
+ app->app_local->home_df = home_df;
+ log_info ("p15: application directory detected as 0x%04hX\n",
+ home_df);
+ /* We assume that direct path selection is possible. */
+ app->app_local->direct_path_selection = 1;
+ }
+
+ /* We only allow a full path if all files are at the same
+ level and below the home directory. To extend this we
+ would need to make use of new data type capable of
+ keeping a full path. */
+ offset = 10;
+ }
+ else
+ {
+ log_printhex (p, buflen, "p15: ODF format not supported:");
+ xfree (buffer);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ switch ((p[0] & 0x0f))
+ {
+ case 0: value = app->app_local->odf.private_keys; break;
+ case 1: value = app->app_local->odf.public_keys; break;
+ case 2: value = app->app_local->odf.trusted_public_keys; break;
+ case 3: value = app->app_local->odf.secret_keys; break;
+ case 4: value = app->app_local->odf.certificates; break;
+ case 5: value = app->app_local->odf.trusted_certificates; break;
+ case 6: value = app->app_local->odf.useful_certificates; break;
+ case 7: value = app->app_local->odf.data_objects; break;
+ case 8: value = app->app_local->odf.auth_objects; break;
+ default: value = 0; break;
+ }
+ if (value)
+ {
+ log_error ("p15: duplicate object type %d in ODF ignored\n",
+ (p[0]&0x0f));
+ continue;
+ }
+ value = ((p[offset] << 8) | p[offset+1]);
+ switch ((p[0] & 0x0f))
+ {
+ case 0: app->app_local->odf.private_keys = value; break;
+ case 1: app->app_local->odf.public_keys = value; break;
+ case 2: app->app_local->odf.trusted_public_keys = value; break;
+ case 3: app->app_local->odf.secret_keys = value; break;
+ case 4: app->app_local->odf.certificates = value; break;
+ case 5: app->app_local->odf.trusted_certificates = value; break;
+ case 6: app->app_local->odf.useful_certificates = value; break;
+ case 7: app->app_local->odf.data_objects = value; break;
+ case 8: app->app_local->odf.auth_objects = value; break;
+ default:
+ log_error ("p15: unknown object type %d in ODF ignored\n",
+ (p[0]&0x0f));
+ }
+ offset += 2;
+
+ if (buflen < offset)
+ break;
+ p += offset;
+ buflen -= offset;
+ }
+
+ if (buflen)
+ {
+ /* Print a warning if non-null garbage is left over. */
+ for (n=0; n < buflen && !p[n]; n++)
+ ;
+ if (n < buflen)
+ {
+ log_info ("p15: warning: garbage detected at end of ODF: ");
+ log_printhex (p, buflen, "");
+ }
+ }
+
+ xfree (buffer);
+ return 0;
+}
+
+
+/* Helper for the read_ef_foo functions to read the first record or
+ * the entire data. */
+static gpg_error_t
+read_first_record (app_t app, unsigned short fid, const char *fid_desc,
+ unsigned char **r_buffer, size_t *r_buflen,
+ int *r_use_read_record)
+{
+ gpg_error_t err;
+ int sw;
+
+ *r_buffer = NULL;
+ *r_buflen = 0;
+ *r_use_read_record = 0;
+
+ if (!fid)
+ return gpg_error (GPG_ERR_NO_DATA); /* No such file. */
+
+ if (IS_CARDOS_5 (app))
+ {
+ *r_use_read_record = 1;
+ err = select_and_read_record (app, fid, 1, fid_desc,
+ r_buffer, r_buflen, &sw);
+ if (err && sw == SW_FILE_STRUCT)
+ {
+ *r_use_read_record = 0;
+ err = select_and_read_binary (app, 0, fid_desc, r_buffer, r_buflen);
+ }
+ }
+ else
+ err = select_and_read_binary (app, fid, fid_desc, r_buffer, r_buflen);
+
+ /* We get a not_found state in read_record mode if the select
+ * succeeded but reading the record failed. Map that to no_data
+ * which is what the caller of the read_ef_foo functions expect. */
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = gpg_error (GPG_ERR_NO_DATA);
+
+ return err;
+}
+
+
+/* Parse the BIT STRING with the keyUsageFlags from the
+ CommonKeyAttributes. */
+static gpg_error_t
+parse_keyusage_flags (const unsigned char *der, size_t derlen,
+ keyusage_flags_t *usageflags)
+{
+ unsigned int bits, mask;
+ int i, unused, full;
+
+ memset (usageflags, 0, sizeof *usageflags);
+ if (!derlen)
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ unused = *der++; derlen--;
+ if ((!derlen && unused) || unused/8 > derlen)
+ return gpg_error (GPG_ERR_ENCODING_PROBLEM);
+ full = derlen - (unused+7)/8;
+ unused %= 8;
+ mask = 0;
+ for (i=1; unused; i <<= 1, unused--)
+ mask |= i;
+
+ /* First octet */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ mask = 0;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x80)) usageflags->encrypt = 1;
+ if ((bits & 0x40)) usageflags->decrypt = 1;
+ if ((bits & 0x20)) usageflags->sign = 1;
+ if ((bits & 0x10)) usageflags->sign_recover = 1;
+ if ((bits & 0x08)) usageflags->wrap = 1;
+ if ((bits & 0x04)) usageflags->unwrap = 1;
+ if ((bits & 0x02)) usageflags->verify = 1;
+ if ((bits & 0x01)) usageflags->verify_recover = 1;
+
+ /* Second octet. */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x80)) usageflags->derive = 1;
+ if ((bits & 0x40)) usageflags->non_repudiation = 1;
+
+ return 0;
+}
+
+
+static void
+dump_keyusage_flags (keyusage_flags_t usageflags)
+{
+ const char *s = "";
+
+ log_info ("p15: usage=");
+ if (usageflags.encrypt)
+ log_printf ("%sencrypt", s), s = ",";
+ if (usageflags.decrypt)
+ log_printf ("%sdecrypt", s), s = ",";
+ if (usageflags.sign )
+ log_printf ("%ssign", s), s = ",";
+ if (usageflags.sign_recover)
+ log_printf ("%ssign_recover", s), s = ",";
+ if (usageflags.wrap )
+ log_printf ("%swrap", s), s = ",";
+ if (usageflags.unwrap )
+ log_printf ("%sunwrap", s), s = ",";
+ if (usageflags.verify )
+ log_printf ("%sverify", s), s = ",";
+ if (usageflags.verify_recover)
+ log_printf ("%sverify_recover", s), s = ",";
+ if (usageflags.derive )
+ log_printf ("%sderive", s), s = ",";
+ if (usageflags.non_repudiation)
+ log_printf ("%snon_repudiation", s), s = ",";
+}
+
+
+static void
+dump_keyaccess_flags (keyaccess_flags_t accessflags)
+{
+ const char *s = "";
+
+ log_info ("p15: access=");
+ if (accessflags.sensitive)
+ log_printf ("%ssensitive", s), s = ",";
+ if (accessflags.extractable)
+ log_printf ("%sextractable", s), s = ",";
+ if (accessflags.always_sensitive)
+ log_printf ("%salways_sensitive", s), s = ",";
+ if (accessflags.never_extractable)
+ log_printf ("%snever_extractable", s), s = ",";
+ if (accessflags.local)
+ log_printf ("%slocal", s), s = ",";
+}
+
+
+static void
+dump_gpgusage_flags (gpgusage_flags_t gpgusage)
+{
+ const char *s = "";
+
+ log_info ("p15: gpgusage=");
+ if (gpgusage.cert)
+ log_printf ("%scert", s), s = ",";
+ if (gpgusage.sign)
+ log_printf ("%ssign", s), s = ",";
+ if (gpgusage.encr)
+ log_printf ("%sencr", s), s = ",";
+ if (gpgusage.auth)
+ log_printf ("%sauth", s), s = ",";
+}
+
+
+/* Parse the BIT STRING with the keyAccessFlags from the
+ CommonKeyAttributes. */
+static gpg_error_t
+parse_keyaccess_flags (const unsigned char *der, size_t derlen,
+ keyaccess_flags_t *accessflags)
+{
+ unsigned int bits, mask;
+ int i, unused, full;
+
+ memset (accessflags, 0, sizeof *accessflags);
+ if (!derlen)
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ unused = *der++; derlen--;
+ if ((!derlen && unused) || unused/8 > derlen)
+ return gpg_error (GPG_ERR_ENCODING_PROBLEM);
+ full = derlen - (unused+7)/8;
+ unused %= 8;
+ mask = 0;
+ for (i=1; unused; i <<= 1, unused--)
+ mask |= i;
+
+ /* First octet */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ mask = 0;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x10)) accessflags->local = 1;
+ if ((bits & 0x08)) accessflags->never_extractable = 1;
+ if ((bits & 0x04)) accessflags->always_sensitive = 1;
+ if ((bits & 0x02)) accessflags->extractable = 1;
+ if ((bits & 0x01)) accessflags->sensitive = 1;
+
+ accessflags->any = 1;
+ return 0;
+}
+
+
+/* Parse the commonObjectAttributes and store a malloced authid at
+ * (r_authid,r_authidlen). (NULL,0) is stored on error or if no
+ * authid is found. IF R_LABEL is not NULL the label is stored there
+ * as a malloced string (spaces are replaced by underscores).
+ *
+ * Example data:
+ * 2 30 17: SEQUENCE { -- commonObjectAttributes
+ * 4 0C 8: UTF8String 'SK.CH.DS' -- label
+ * 14 03 2: BIT STRING 6 unused bits
+ * : '01'B (bit 0)
+ * 18 04 1: OCTET STRING --authid
+ * : 07
+ * : }
+ */
+static gpg_error_t
+parse_common_obj_attr (unsigned char const **buffer, size_t *size,
+ unsigned char **r_authid, size_t *r_authidlen,
+ char **r_label)
+{
+ gpg_error_t err;
+ int where;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen, nnn;
+ const unsigned char *ppp;
+ int ignore_eof = 0;
+ char *p;
+
+ *r_authid = NULL;
+ *r_authidlen = 0;
+ if (r_label)
+ *r_label = NULL;
+
+ where = __LINE__;
+ err = parse_ber_header (buffer, size, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > *size || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ ppp = *buffer;
+ nnn = objlen;
+ *buffer += objlen;
+ *size -= objlen;
+
+ /* Search the optional AuthId. */
+ ignore_eof = 1;
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ if (tag == TAG_UTF8_STRING)
+ {
+ if (r_label)
+ {
+ *r_label = xtrymalloc (objlen + 1);
+ if (!*r_label)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (*r_label, ppp, objlen);
+ (*r_label)[objlen] = 0;
+ /* We don't want spaces in the labels due to the properties
+ * of CHV-LABEL. */
+ for (p = *r_label; *p; p++)
+ if (ascii_isspace (*p))
+ *p = '_';
+ }
+
+ ppp += objlen;
+ nnn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (tag == TAG_BIT_STRING)
+ {
+ ppp += objlen; /* Skip the CommonObjectFlags. */
+ nnn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (tag == TAG_OCTET_STRING && objlen)
+ {
+ *r_authid = xtrymalloc (objlen);
+ if (!*r_authid)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (*r_authid, ppp, objlen);
+ *r_authidlen = objlen;
+ }
+
+ leave:
+ if (ignore_eof && gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ else if (err)
+ log_error ("p15: error parsing commonObjectAttributes at %d: %s\n",
+ where, gpg_strerror (err));
+
+ if (err && r_label)
+ {
+ xfree (*r_label);
+ *r_label = NULL;
+ }
+
+ return err;
+}
+
+
+/* Parse the commonKeyAttributes. On success store the objid at
+ * (R_OBJID/R_OBJIDLEN), sets the key usage flags at USAGEFLAGS and
+ * the optiona key refrence at R_KEY_REFERENCE. The latter is only
+ * valid if true is also stored at R_KEY_REFERENCE_VALID.
+ *
+ * Example data:
+ *
+ * 21 30 12: SEQUENCE { -- commonKeyAttributes
+ * 23 04 1: OCTET STRING
+ * : 01
+ * 26 03 3: BIT STRING 6 unused bits
+ * : '1000000000'B (bit 9)
+ * 31 02 2: INTEGER 80 -- keyReference (optional)
+ * : }
+ */
+static gpg_error_t
+parse_common_key_attr (unsigned char const **buffer, size_t *size,
+ unsigned char **r_objid, size_t *r_objidlen,
+ keyusage_flags_t *usageflags,
+ keyaccess_flags_t *accessflags,
+ unsigned long *r_key_reference,
+ int *r_key_reference_valid)
+{
+ gpg_error_t err;
+ int where;
+ int class, tag, constructed, ndef;
+ size_t objlen, hdrlen, nnn;
+ const unsigned char *ppp;
+ int ignore_eof = 0;
+ unsigned long ul;
+ const unsigned char *objid = NULL;
+ size_t objidlen;
+ unsigned long key_reference = 0;
+ int key_reference_valid = 0;
+
+ *r_objid = NULL;
+ *r_objidlen = 0;
+ memset (usageflags, 0, sizeof *usageflags);
+ memset (accessflags, 0, sizeof *accessflags);
+ *r_key_reference_valid = 0;
+
+ where = __LINE__;
+ err = parse_ber_header (buffer, size, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > *size || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ ppp = *buffer;
+ nnn = objlen;
+ *buffer += objlen;
+ *size -= objlen;
+
+ /* Get the Id. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ objid = ppp;
+ objidlen = objlen;
+ ppp += objlen;
+ nnn -= objlen;
+
+ /* Get the KeyUsageFlags. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ err = parse_keyusage_flags (ppp, objlen, usageflags);
+ if (err)
+ goto leave;
+ ppp += objlen;
+ nnn -= objlen;
+
+ ignore_eof = 1; /* Remaining items are optional. */
+
+ /* Find the keyReference */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN)
+ {
+ /* Skip the native element. */
+ ppp += objlen;
+ nnn -= objlen;
+
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
+ {
+ /* These are the keyAccessFlags. */
+ err = parse_keyaccess_flags (ppp, objlen, accessflags);
+ if (err)
+ goto leave;
+ ppp += objlen;
+ nnn -= objlen;
+
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
+ {
+ /* This is the keyReference. */
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*ppp++) & 0xff;
+ nnn--;
+ }
+ key_reference = ul;
+ key_reference_valid = 1;
+ }
+
+ leave:
+ if (ignore_eof && gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+
+ if (!err)
+ {
+ if (!objid || !objidlen)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else
+ {
+ *r_objid = xtrymalloc (objidlen);
+ if (!*r_objid)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ memcpy (*r_objid, objid, objidlen);
+ *r_objidlen = objidlen;
+ }
+ }
+ }
+ if (!err && key_reference_valid)
+ {
+ *r_key_reference = key_reference;
+ *r_key_reference_valid = 1;
+ }
+
+ if (err)
+ log_error ("p15: error parsing commonKeyAttributes at %d: %s\n",
+ where, gpg_strerror (err));
+ return err;
+
+}
+
+
+/* Read and parse the Private Key Directory Files.
+ *
+ * Sample object:
+ * SEQUENCE {
+ * SEQUENCE { -- commonObjectAttributes
+ * UTF8String 'SK.CH.DS'
+ * BIT STRING 6 unused bits
+ * '01'B (bit 0) -- flags: non-modifiable,private
+ * OCTET STRING --authid
+ * 07
+ * }
+ * SEQUENCE { -- commonKeyAttributes
+ * OCTET STRING
+ * 01
+ * BIT STRING 6 unused bits
+ * '1000000000'B (bit 9) -- keyusage: non-repudiation
+ * INTEGER 80 -- keyReference (optional)
+ * }
+ * [1] { -- keyAttributes
+ * SEQUENCE { -- privateRSAKeyAttributes
+ * SEQUENCE { -- objectValue
+ * OCTET STRING --path
+ * 3F 00 40 16 00 50
+ * }
+ * INTEGER 1024 -- modulus
+ * }
+ * }
+ * }
+ *
+ * Sample part for EC objects:
+ * [1] { -- keyAttributes
+ * [1] { -- privateECkeyAttributes
+ * SEQUENCE { -- objectValue
+ * SEQUENCE { --path
+ * OCTET STRING 50 72 4B 03
+ * }
+ * INTEGER 33 -- Not in PKCS#15v1.1, need to buy 7816-15?
+ * }
+ * }
+ */
+static gpg_error_t
+read_ef_prkdf (app_t app, unsigned short fid, prkdf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ prkdf_object_t prkdflist = NULL;
+ int i;
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ unsigned char *objid = NULL;
+ size_t objidlen = 0;
+ char *label = NULL;
+ int record_mode;
+
+ err = read_first_record (app, fid, "PrKDF", &buffer, &buflen, &record_mode);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ /* Loop over the records. We stop as soon as we detect a new record
+ starting with 0x00 or 0xff as these values are commonly used to
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
+ while (n && *p && *p != 0xff)
+ {
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ prkdf_object_t prkdf = NULL;
+ unsigned long ul;
+ keyusage_flags_t usageflags;
+ keyaccess_flags_t accessflags;
+ unsigned long key_reference = 0;
+ int key_reference_valid = 0;
+ int is_ecc = 0;
+
+ where = __LINE__;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (objlen > n)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* PrivateRSAKeyAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: is_ecc = 1; break; /* PrivateECKeyAttributes */
+ case 1: errstr = "DH key objects are not supported"; break;
+ case 2: errstr = "DSA key objects are not supported"; break;
+ case 3: errstr = "KEA key objects are not supported"; break;
+ default: errstr = "unknown privateKeyObject"; break;
+ }
+ if (errstr)
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
+ if (err)
+ {
+ log_error ("p15: error parsing PrKDF record: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
+ if (err)
+ goto parse_error;
+
+ /* Parse the commonKeyAttributes. */
+ where = __LINE__;
+ xfree (objid);
+ err = parse_common_key_attr (&pp, &nn,
+ &objid, &objidlen,
+ &usageflags, &accessflags,
+ &key_reference, &key_reference_valid);
+ if (err)
+ goto parse_error;
+ log_assert (objid);
+
+ /* Skip commonPrivateKeyAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ pp += objlen;
+ nn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ }
+ /* Parse the keyAttributes. */
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence. */
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ nn = objlen;
+
+ /* Check that the reference is a Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
+ {
+ errstr = "unsupported reference type";
+ goto parse_error;
+ }
+ nn = objlen;
+
+ /* Parse the Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ /* Make sure that the next element is a non zero path and of
+ even length (FID are two bytes each). */
+ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
+ || !objlen || (objlen & 1) )
+ {
+ errstr = "invalid path reference";
+ goto parse_error;
+ }
+
+ /* Create a new PrKDF list item. */
+ prkdf = xtrycalloc (1, (sizeof *prkdf
+ - sizeof(unsigned short)
+ + objlen/2 * sizeof(unsigned short)));
+ if (!prkdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ prkdf->is_ecc = is_ecc;
+
+ prkdf->objidlen = objidlen;
+ prkdf->objid = objid;
+ objid = NULL;
+ if (authid)
+ {
+ prkdf->authidlen = authidlen;
+ prkdf->authid = authid;
+ authid = NULL;
+ }
+ if (label)
+ {
+ prkdf->label = label;
+ label = NULL;
+ }
+
+ prkdf->pathlen = objlen/2;
+ for (i=0; i < prkdf->pathlen; i++, pp += 2, nn -= 2)
+ prkdf->path[i] = ((pp[0] << 8) | pp[1]);
+
+ prkdf->usageflags = usageflags;
+ prkdf->accessflags = accessflags;
+ prkdf->key_reference = key_reference;
+ prkdf->key_reference_valid = key_reference_valid;
+
+ if (nn)
+ {
+ /* An index and length follows. */
+ prkdf->have_off = 1;
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ prkdf->off = ul;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_CONTEXT || tag != 0))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ prkdf->len = ul;
+ }
+
+ /* The info is printed later in read_p15_info because we also
+ * want to look at the certificates. */
+
+ /* Put it into the list. */
+ prkdf->next = prkdflist;
+ prkdflist = prkdf;
+ prkdf = NULL;
+ goto next_record; /* Ready with this record. */
+
+ parse_error:
+ log_error ("p15: error parsing PrKDF record at %d: %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ if (prkdf)
+ {
+ xfree (prkdf->objid);
+ xfree (prkdf->authid);
+ xfree (prkdf->label);
+ xfree (prkdf);
+ }
+ err = 0;
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "PrKDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End looping over all records. */
+
+ leave:
+ xfree (authid);
+ xfree (label);
+ xfree (objid);
+ xfree (buffer);
+ if (err)
+ release_prkdflist (prkdflist);
+ else
+ *result = prkdflist;
+ return err;
+}
+
+
+/* Read and parse the Public Keys Directory File. */
+static gpg_error_t
+read_ef_pukdf (app_t app, unsigned short fid, pukdf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ pukdf_object_t pukdflist = NULL;
+ int i;
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ unsigned char *objid = NULL;
+ size_t objidlen = 0;
+ char *label = NULL;
+ int record_mode;
+
+ err = read_first_record (app, fid, "PuKDF", &buffer, &buflen, &record_mode);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ /* Loop over the records. We stop as soon as we detect a new record
+ * starting with 0x00 or 0xff as these values are commonly used to
+ * pad data blocks and are no valid ASN.1 encoding. Note the
+ * special handling for record mode at the end of the loop. */
+ while (n && *p && *p != 0xff)
+ {
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ pukdf_object_t pukdf = NULL;
+ unsigned long ul;
+ keyusage_flags_t usageflags;
+ keyaccess_flags_t accessflags;
+ unsigned long key_reference = 0;
+ int key_reference_valid = 0;
+
+ where = __LINE__;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (objlen > n)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* PublicRSAKeyAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: break; /* EC key object */
+ case 1: errstr = "DH key objects are not supported"; break;
+ case 2: errstr = "DSA key objects are not supported"; break;
+ case 3: errstr = "KEA key objects are not supported"; break;
+ default: errstr = "unknown publicKeyObject"; break;
+ }
+ if (errstr)
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
+ if (err)
+ {
+ log_error ("p15: error parsing PuKDF record: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
+ if (err)
+ goto parse_error;
+
+ /* Parse the commonKeyAttributes. */
+ where = __LINE__;
+ xfree (objid);
+ err = parse_common_key_attr (&pp, &nn,
+ &objid, &objidlen,
+ &usageflags, &accessflags,
+ &key_reference, &key_reference_valid);
+ if (err)
+ goto parse_error;
+ log_assert (objid);
+
+ /* Parse the subClassAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ /* Skip this CommonPublicKeyAttribute. */
+ pp += objlen;
+ nn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ }
+ /* We expect a typeAttribute. */
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error; /* No typeAttribute. */
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* A typeAttribute always starts with a sequence. */
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ nn = objlen;
+
+ /* Check that the reference is a Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
+ {
+ errstr = "unsupported reference type";
+ goto parse_error;
+ }
+ nn = objlen;
+
+ /* Parse the Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ /* Make sure that the next element is a non zero path and of
+ even length (FID are two bytes each). */
+ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
+ || !objlen || (objlen & 1) )
+ {
+ errstr = "invalid path reference";
+ goto parse_error;
+ }
+
+ /* Create a new PuKDF list item. */
+ pukdf = xtrycalloc (1, (sizeof *pukdf
+ - sizeof(unsigned short)
+ + objlen/2 * sizeof(unsigned short)));
+ if (!pukdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ pukdf->objidlen = objidlen;
+ pukdf->objid = objid;
+ objid = NULL;
+ if (authid)
+ {
+ pukdf->authidlen = authidlen;
+ pukdf->authid = authid;
+ authid = NULL;
+ }
+ if (label)
+ {
+ pukdf->label = label;
+ label = NULL;
+ }
+
+ pukdf->pathlen = objlen/2;
+ for (i=0; i < pukdf->pathlen; i++, pp += 2, nn -= 2)
+ pukdf->path[i] = ((pp[0] << 8) | pp[1]);
+
+ pukdf->usageflags = usageflags;
+ pukdf->accessflags = accessflags;
+ pukdf->key_reference = key_reference;
+ pukdf->key_reference_valid = key_reference_valid;
+
+ if (nn)
+ {
+ /* An index and length follows. */
+ pukdf->have_off = 1;
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ pukdf->off = ul;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_CONTEXT || tag != 0))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ pukdf->len = ul;
+ }
+
+
+ if (opt.verbose)
+ {
+ log_info ("p15: PuKDF %04hX: id=", fid);
+ for (i=0; i < pukdf->objidlen; i++)
+ log_printf ("%02X", pukdf->objid[i]);
+ if (pukdf->label)
+ log_printf (" (%s)", pukdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < pukdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"",pukdf->path[i]);
+ if (pukdf->have_off)
+ log_printf ("[%lu/%lu]", pukdf->off, pukdf->len);
+ if (pukdf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < pukdf->authidlen; i++)
+ log_printf ("%02X", pukdf->authid[i]);
+ }
+ if (pukdf->key_reference_valid)
+ log_printf (" keyref=0x%02lX", pukdf->key_reference);
+ if (pukdf->accessflags.any)
+ dump_keyaccess_flags (pukdf->accessflags);
+ dump_keyusage_flags (pukdf->usageflags);
+ log_printf ("\n");
+ }
+
+ /* Put it into the list. */
+ pukdf->next = pukdflist;
+ pukdflist = pukdf;
+ pukdf = NULL;
+ goto next_record; /* Ready with this record. */
+
+ parse_error:
+ log_error ("p15: error parsing PuKDF record at %d: %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ if (pukdf)
+ {
+ xfree (pukdf->objid);
+ xfree (pukdf->authid);
+ xfree (pukdf->label);
+ xfree (pukdf);
+ }
+ err = 0;
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "PuKDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End looping over all records. */
+
+ leave:
+ xfree (authid);
+ xfree (label);
+ xfree (objid);
+ xfree (buffer);
+ if (err)
+ release_pukdflist (pukdflist);
+ else
+ *result = pukdflist;
+ return err;
+}
+
+
+/* Read and parse the Certificate Directory Files identified by FID.
+ On success a newlist of CDF object gets stored at RESULT and the
+ caller is then responsible of releasing this list. On error a
+ error code is returned and RESULT won't get changed. */
+static gpg_error_t
+read_ef_cdf (app_t app, unsigned short fid, int cdftype, cdf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ cdf_object_t cdflist = NULL;
+ int i;
+ int recno = 1;
+ unsigned char *authid = NULL;
+ size_t authidlen = 0;
+ char *label = NULL;
+ int record_mode;
+
+ err = read_first_record (app, fid, "CDF", &buffer, &buflen, &record_mode);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ /* Loop over the records. We stop as soon as we detect a new record
+ starting with 0x00 or 0xff as these values are commonly used to
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
+ while (n && *p && *p != 0xff)
+ {
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ cdf_object_t cdf = NULL;
+ unsigned long ul;
+ const unsigned char *objid;
+ size_t objidlen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ log_error ("p15: error parsing CDF record: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ xfree (authid);
+ xfree (label);
+ err = parse_common_obj_attr (&pp, &nn, &authid, &authidlen, &label);
+ if (err)
+ goto parse_error;
+
+ /* Parse the commonCertificateAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Get the Id. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ objid = ppp;
+ objidlen = objlen;
+ }
+
+ /* Parse the certAttribute. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ /* Check that the reference is a Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
+ {
+ errstr = "unsupported reference type";
+ goto parse_error;
+ }
+ nn = objlen;
+
+ /* Parse the Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ /* Make sure that the next element is a non zero path and of
+ even length (FID are two bytes each). */
+ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
+ || !objlen || (objlen & 1) )
+ {
+ errstr = "invalid path reference";
+ goto parse_error;
+ }
+ /* Create a new CDF list item. */
+ cdf = xtrycalloc (1, (sizeof *cdf
+ - sizeof(unsigned short)
+ + objlen/2 * sizeof(unsigned short)));
+ if (!cdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (authid)
+ {
+ cdf->authidlen = authidlen;
+ cdf->authid = authid;
+ authid = NULL;
+ }
+ if (label)
+ {
+ cdf->label = label;
+ label = NULL;
+ }
+
+ cdf->objidlen = objidlen;
+ cdf->objid = xtrymalloc (objidlen);
+ if (!cdf->objid)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (cdf);
+ goto leave;
+ }
+ memcpy (cdf->objid, objid, objidlen);
+
+ cdf->pathlen = objlen/2;
+ for (i=0; i < cdf->pathlen; i++, pp += 2, nn -= 2)
+ cdf->path[i] = ((pp[0] << 8) | pp[1]);
+
+ if (nn)
+ {
+ /* An index and length follows. */
+ cdf->have_off = 1;
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ cdf->off = ul;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_CONTEXT || tag != 0))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ cdf->len = ul;
+ }
+
+ if (opt.verbose)
+ {
+ log_info ("p15: CDF-%c %04hX: id=", cdftype, fid);
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+ if (cdf->label)
+ log_printf (" (%s)", cdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < cdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"", cdf->path[i]);
+ if (cdf->have_off)
+ log_printf ("[%lu/%lu]", cdf->off, cdf->len);
+ if (cdf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < cdf->authidlen; i++)
+ log_printf ("%02X", cdf->authid[i]);
+ }
+ log_printf ("\n");
+ }
+
+ /* Put it into the list. */
+ cdf->next = cdflist;
+ cdflist = cdf;
+ cdf = NULL;
+ goto next_record; /* Ready with this record. */
+
+ parse_error:
+ log_error ("p15: error parsing CDF record at %d: %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ xfree (cdf);
+ err = 0;
+
+ next_record:
+ xfree (authid);
+ xfree (label);
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "CDF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End loop over all records. */
+
+ leave:
+ xfree (authid);
+ xfree (label);
+ xfree (buffer);
+ if (err)
+ release_cdflist (cdflist);
+ else
+ *result = cdflist;
+ return err;
+}
+
+
+/*
+ * SEQUENCE {
+ * SEQUENCE { -- CommonObjectAttributes
+ * UTF8String 'specific PIN for DS'
+ * BIT STRING 0 unused bits
+ * '00000011'B
+ * }
+ * SEQUENCE { -- CommonAuthenticationObjectAttributes
+ * OCTET STRING
+ * 07 -- iD
+ * }
+ *
+ * [1] { -- typeAttributes
+ * SEQUENCE { -- PinAttributes
+ * BIT STRING 0 unused bits
+ * '0000100000110010'B -- local,initialized,needs-padding
+ * -- exchangeRefData
+ * ENUMERATED 1 -- ascii-numeric
+ * INTEGER 6 -- minLength
+ * INTEGER 6 -- storedLength
+ * INTEGER 8 -- maxLength
+ * [0]
+ * 02 -- pinReference
+ * GeneralizedTime 19/04/2002 12:12 GMT -- lastPinChange
+ * SEQUENCE {
+ * OCTET STRING
+ * 3F 00 40 16 -- path to DF of PIN
+ * }
+ * }
+ * }
+ * }
+ *
+ * Or for an authKey:
+ *
+ * [1] { -- typeAttributes
+ * SEQUENCE { -- AuthKeyAttributes
+ * BOOLEAN TRUE -- derivedKey
+ * OCTET STRING 02 -- authKeyId
+ * }
+ * }
+ * }
+*/
+/* Read and parse an Authentication Object Directory File identified
+ by FID. On success a newlist of AODF objects gets stored at RESULT
+ and the caller is responsible of releasing this list. On error a
+ error code is returned and RESULT won't get changed. */
+static gpg_error_t
+read_ef_aodf (app_t app, unsigned short fid, aodf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ aodf_object_t aodflist = NULL;
+ int i;
+ int recno = 1;
+ int record_mode;
+
+ err = read_first_record (app, fid, "AODF", &buffer, &buflen, &record_mode);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ /* Loop over the records. We stop as soon as we detect a new record
+ starting with 0x00 or 0xff as these values are commonly used to
+ pad data blocks and are no valid ASN.1 encoding. Note the
+ special handling for record mode at the end of the loop. */
+ while (n && *p && *p != 0xff)
+ {
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ auth_type_t auth_type;
+ aodf_object_t aodf = NULL;
+ unsigned long ul;
+ const char *s;
+
+ where = __LINE__;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (objlen > n)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ auth_type = AUTH_TYPE_PIN; /* PinAttributes */
+ else if (class == CLASS_CONTEXT && tag == 1 )
+ auth_type = AUTH_TYPE_AUTHKEY; /* AuthKeyAttributes */
+ else if (class == CLASS_CONTEXT)
+ {
+ switch (tag)
+ {
+ case 0: errstr = "biometric auth types are not supported"; break;
+ case 2: errstr = "external auth type are not supported"; break;
+ default: errstr = "unknown privateKeyObject"; break;
+ }
+ goto parse_error;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+
+ if (err)
+ {
+ log_error ("p15: error parsing AODF record: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Allocate memory for a new AODF list item. */
+ aodf = xtrycalloc (1, sizeof *aodf);
+ if (!aodf)
+ goto no_core;
+ aodf->fid = fid;
+ aodf->auth_type = auth_type;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ err = parse_common_obj_attr (&pp, &nn, &aodf->authid, &aodf->authidlen,
+ &aodf->label);
+ if (err)
+ goto parse_error;
+
+ /* Parse the CommonAuthenticationObjectAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Get the Id. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ aodf->objidlen = objlen;
+ aodf->objid = xtrymalloc (objlen);
+ if (!aodf->objid)
+ goto no_core;
+ memcpy (aodf->objid, ppp, objlen);
+ }
+
+ /* Parse the typeAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ ;
+ else if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ else if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE)
+ ; /* Okay */
+ else
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ nn = objlen;
+
+ if (auth_type == AUTH_TYPE_PIN)
+ {
+ /* PinFlags */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || !objlen
+ || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ {
+ unsigned int bits, mask;
+ int unused, full;
+
+ unused = *pp++; nn--; objlen--;
+ if ((!objlen && unused) || unused/8 > objlen)
+ {
+ err = gpg_error (GPG_ERR_ENCODING_PROBLEM);
+ goto parse_error;
+ }
+ full = objlen - (unused+7)/8;
+ unused %= 8;
+ mask = 0;
+ for (i=1; unused; i <<= 1, unused--)
+ mask |= i;
+
+ /* The first octet */
+ bits = 0;
+ if (objlen)
+ {
+ bits = *pp++; nn--; objlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ mask = 0;
+ }
+ }
+ if ((bits & 0x80)) /* ASN.1 bit 0. */
+ aodf->pinflags.case_sensitive = 1;
+ if ((bits & 0x40)) /* ASN.1 bit 1. */
+ aodf->pinflags.local = 1;
+ if ((bits & 0x20))
+ aodf->pinflags.change_disabled = 1;
+ if ((bits & 0x10))
+ aodf->pinflags.unblock_disabled = 1;
+ if ((bits & 0x08))
+ aodf->pinflags.initialized = 1;
+ if ((bits & 0x04))
+ aodf->pinflags.needs_padding = 1;
+ if ((bits & 0x02))
+ aodf->pinflags.unblocking_pin = 1;
+ if ((bits & 0x01))
+ aodf->pinflags.so_pin = 1;
+ /* The second octet. */
+ bits = 0;
+ if (objlen)
+ {
+ bits = *pp++; nn--; objlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ }
+ }
+ if ((bits & 0x80))
+ aodf->pinflags.disable_allowed = 1;
+ if ((bits & 0x40))
+ aodf->pinflags.integrity_protected = 1;
+ if ((bits & 0x20))
+ aodf->pinflags.confidentiality_protected = 1;
+ if ((bits & 0x10))
+ aodf->pinflags.exchange_ref_data = 1;
+ /* Skip remaining bits. */
+ pp += objlen;
+ nn -= objlen;
+ }
+
+ /* PinType */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_ENUMERATED))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (!err && objlen > sizeof (ul))
+ err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ aodf->pintype = ul;
+
+ /* minLength */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (!err && objlen > sizeof (ul))
+ err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+ if (err)
+ goto parse_error;
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ aodf->min_length = ul;
+
+ /* storedLength */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (!err && objlen > sizeof (ul))
+ err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+ if (err)
+ goto parse_error;
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ aodf->stored_length = ul;
+
+ /* optional maxLength */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto ready;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
+ {
+ if (objlen > sizeof (ul))
+ {
+ err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+ goto parse_error;
+ }
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ aodf->max_length = ul;
+ aodf->max_length_valid = 1;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto ready;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+
+ /* Optional pinReference. */
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ if (objlen > sizeof (ul))
+ {
+ err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
+ goto parse_error;
+ }
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*pp++) & 0xff;
+ nn--;
+ }
+ aodf->pin_reference = ul;
+ aodf->pin_reference_valid = 1;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto ready;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+
+ /* Optional padChar. */
+ if (class == CLASS_UNIVERSAL && tag == TAG_OCTET_STRING)
+ {
+ if (objlen != 1)
+ {
+ errstr = "padChar is not of size(1)";
+ goto parse_error;
+ }
+ aodf->pad_char = *pp++; nn--;
+ aodf->pad_char_valid = 1;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto ready;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+
+ /* Skip optional lastPinChange. */
+ if (class == CLASS_UNIVERSAL && tag == TAG_GENERALIZED_TIME)
+ {
+ pp += objlen;
+ nn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto ready;
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+
+ /* Optional Path object. */
+ if (class == CLASS_UNIVERSAL || tag == TAG_SEQUENCE)
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ /* Make sure that the next element has a path of even
+ * length (FIDs are two bytes each). */
+ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
+ || (objlen & 1) )
+ {
+ errstr = "invalid path reference";
+ goto parse_error;
+ }
+
+ aodf->pathlen = objlen/2;
+ aodf->path = xtrycalloc (aodf->pathlen, sizeof *aodf->path);
+ if (!aodf->path)
+ goto no_core;
+ for (i=0; i < aodf->pathlen; i++, ppp += 2, nnn -= 2)
+ aodf->path[i] = ((ppp[0] << 8) | ppp[1]);
+
+ if (nnn)
+ {
+ /* An index and length follows. */
+ aodf->have_off = 1;
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag,
+ &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL
+ || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*ppp++) & 0xff;
+ nnn--;
+ }
+ aodf->off = ul;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag,
+ &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_CONTEXT || tag != 0))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*ppp++) & 0xff;
+ nnn--;
+ }
+ aodf->len = ul;
+ }
+ }
+ }
+ else if (auth_type == AUTH_TYPE_AUTHKEY)
+ {
+
+ }
+
+ /* Ignore further objects which might be there due to future
+ extensions of pkcs#15. */
+
+ ready:
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ if (opt.verbose)
+ {
+ log_info ("p15: AODF %04hX: id=", fid);
+ for (i=0; i < aodf->objidlen; i++)
+ log_printf ("%02X", aodf->objid[i]);
+ if (aodf->label)
+ log_printf (" (%s)", aodf->label);
+ log_info ("p15: ");
+ log_printf (" %s",
+ aodf->auth_type == AUTH_TYPE_PIN? "pin" :
+ aodf->auth_type == AUTH_TYPE_AUTHKEY? "authkey" : "?");
+ if (aodf->pathlen)
+ {
+ log_printf (" path=");
+ for (i=0; i < aodf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"",aodf->path[i]);
+ if (aodf->have_off)
+ log_printf ("[%lu/%lu]", aodf->off, aodf->len);
+ }
+ if (aodf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < aodf->authidlen; i++)
+ log_printf ("%02X", aodf->authid[i]);
+ }
+ if (aodf->auth_type == AUTH_TYPE_PIN)
+ {
+ if (aodf->pin_reference_valid)
+ log_printf (" pinref=0x%02lX", aodf->pin_reference);
+ log_printf (" min=%lu", aodf->min_length);
+ log_printf (" stored=%lu", aodf->stored_length);
+ if (aodf->max_length_valid)
+ log_printf (" max=%lu", aodf->max_length);
+ if (aodf->pad_char_valid)
+ log_printf (" pad=0x%02x", aodf->pad_char);
+
+ log_info ("p15: flags=");
+ s = "";
+ if (aodf->pinflags.case_sensitive)
+ log_printf ("%scase_sensitive", s), s = ",";
+ if (aodf->pinflags.local)
+ log_printf ("%slocal", s), s = ",";
+ if (aodf->pinflags.change_disabled)
+ log_printf ("%schange_disabled", s), s = ",";
+ if (aodf->pinflags.unblock_disabled)
+ log_printf ("%sunblock_disabled", s), s = ",";
+ if (aodf->pinflags.initialized)
+ log_printf ("%sinitialized", s), s = ",";
+ if (aodf->pinflags.needs_padding)
+ log_printf ("%sneeds_padding", s), s = ",";
+ if (aodf->pinflags.unblocking_pin)
+ log_printf ("%sunblocking_pin", s), s = ",";
+ if (aodf->pinflags.so_pin)
+ log_printf ("%sso_pin", s), s = ",";
+ if (aodf->pinflags.disable_allowed)
+ log_printf ("%sdisable_allowed", s), s = ",";
+ if (aodf->pinflags.integrity_protected)
+ log_printf ("%sintegrity_protected", s), s = ",";
+ if (aodf->pinflags.confidentiality_protected)
+ log_printf ("%sconfidentiality_protected", s), s = ",";
+ if (aodf->pinflags.exchange_ref_data)
+ log_printf ("%sexchange_ref_data", s), s = ",";
+ {
+ char numbuf[50];
+ const char *s2;
+
+ switch (aodf->pintype)
+ {
+ case PIN_TYPE_BCD: s2 = "bcd"; break;
+ case PIN_TYPE_ASCII_NUMERIC: s2 = "ascii-numeric"; break;
+ case PIN_TYPE_UTF8: s2 = "utf8"; break;
+ case PIN_TYPE_HALF_NIBBLE_BCD: s2 = "half-nibble-bcd"; break;
+ case PIN_TYPE_ISO9564_1: s2 = "iso9564-1"; break;
+ default:
+ sprintf (numbuf, "%lu", (unsigned long)aodf->pintype);
+ s2 = numbuf;
+ }
+ log_printf ("%stype=%s", s, s2); s = ",";
+ }
+ }
+ else if (aodf->auth_type == AUTH_TYPE_AUTHKEY)
+ {
+ }
+ log_printf ("\n");
+ }
+
+ /* Put it into the list. */
+ aodf->next = aodflist;
+ aodflist = aodf;
+ aodf = NULL;
+ goto next_record; /* Ready with this record. */
+
+ no_core:
+ err = gpg_error_from_syserror ();
+ release_aodf_object (aodf);
+ goto leave;
+
+ parse_error:
+ log_error ("p15: error parsing AODF record at %d: %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ err = 0;
+ release_aodf_object (aodf);
+
+ next_record:
+ /* If the card uses a record oriented file structure, read the
+ * next record. Otherwise we keep on parsing the current buffer. */
+ recno++;
+ if (record_mode)
+ {
+ xfree (buffer); buffer = NULL;
+ err = select_and_read_record (app, 0, recno, "AODF",
+ &buffer, &buflen, NULL);
+ if (err) {
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = 0;
+ goto leave;
+ }
+ p = buffer;
+ n = buflen;
+ }
+ } /* End looping over all records. */
+
+ leave:
+ xfree (buffer);
+ if (err)
+ release_aodflist (aodflist);
+ else
+ *result = aodflist;
+ return err;
+}
+
+
+/* Print the BIT STRING with the tokenflags from the TokenInfo. */
+static void
+print_tokeninfo_tokenflags (const unsigned char *der, size_t derlen)
+{
+ unsigned int bits, mask;
+ int i, unused, full;
+ int other = 0;
+
+ if (!derlen)
+ {
+ log_printf (" [invalid object]");
+ return;
+ }
+
+ unused = *der++; derlen--;
+ if ((!derlen && unused) || unused/8 > derlen)
+ {
+ log_printf (" [wrong encoding]");
+ return;
+ }
+ full = derlen - (unused+7)/8;
+ unused %= 8;
+ mask = 0;
+ for (i=1; unused; i <<= 1, unused--)
+ mask |= i;
+
+ /* First octet */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ mask = 0;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x80)) log_printf (" readonly");
+ if ((bits & 0x40)) log_printf (" loginRequired");
+ if ((bits & 0x20)) log_printf (" prnGeneration");
+ if ((bits & 0x10)) log_printf (" eidCompliant");
+ if ((bits & 0x08)) other = 1;
+ if ((bits & 0x04)) other = 1;
+ if ((bits & 0x02)) other = 1;
+ if ((bits & 0x01)) other = 1;
+
+ /* Next octet. */
+ if (derlen)
+ other = 1;
+
+ if (other)
+ log_printf (" [unknown]");
+}
+
+
+
+/* Read and parse the EF(TokenInfo).
+ *
+ * TokenInfo ::= SEQUENCE {
+ * version INTEGER {v1(0)} (v1,...),
+ * serialNumber OCTET STRING,
+ * manufacturerID Label OPTIONAL,
+ * label [0] Label OPTIONAL,
+ * tokenflags TokenFlags,
+ * seInfo SEQUENCE OF SecurityEnvironmentInfo OPTIONAL,
+ * recordInfo [1] RecordInfo OPTIONAL,
+ * supportedAlgorithms [2] SEQUENCE OF AlgorithmInfo OPTIONAL,
+ * ...,
+ * issuerId [3] Label OPTIONAL,
+ * holderId [4] Label OPTIONAL,
+ * lastUpdate [5] LastUpdate OPTIONAL,
+ * preferredLanguage PrintableString OPTIONAL -- In accordance with
+ * -- IETF RFC 1766
+ * } (CONSTRAINED BY { -- Each AlgorithmInfo.reference value must be unique --})
+ *
+ * TokenFlags ::= BIT STRING {
+ * readOnly (0),
+ * loginRequired (1),
+ * prnGeneration (2),
+ * eidCompliant (3)
+ * }
+ *
+ *
+ * Sample EF 5032:
+ * 30 31 02 01 00 04 04 05 45 36 9F 0C 0C 44 2D 54 01......E6...D-T
+ * 72 75 73 74 20 47 6D 62 48 80 14 4F 66 66 69 63 rust GmbH..Offic
+ * 65 20 69 64 65 6E 74 69 74 79 20 63 61 72 64 03 e identity card.
+ * 02 00 40 20 63 61 72 64 03 02 00 40 00 00 00 00 ..@ card...@....
+ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ *
+ * 0 49: SEQUENCE {
+ * 2 1: INTEGER 0
+ * 5 4: OCTET STRING 05 45 36 9F
+ * 11 12: UTF8String 'D-Trust GmbH'
+ * 25 20: [0] 'Office identity card'
+ * 47 2: BIT STRING
+ * : '00000010'B (bit 1)
+ * : Error: Spurious zero bits in bitstring.
+ * : }
+ */
+static gpg_error_t
+read_ef_tokeninfo (app_t app)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ unsigned long ul;
+
+ release_tokeninfo (app);
+ app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
+
+ err = select_and_read_binary (app, 0x5032, "TokenInfo", &buffer, &buflen);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ log_error ("p15: error parsing TokenInfo: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ n = objlen;
+
+ /* Version. */
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_INTEGER))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*p++) & 0xff;
+ n--;
+ }
+ if (ul)
+ {
+ log_error ("p15: invalid version %lu in TokenInfo\n", ul);
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+
+ /* serialNumber. */
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_OCTET_STRING || !objlen))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+
+ xfree (app->app_local->serialno);
+ app->app_local->serialno = xtrymalloc (objlen);
+ if (!app->app_local->serialno)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (app->app_local->serialno, p, objlen);
+ app->app_local->serialnolen = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Is there an optional manufacturerID? */
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || !objlen))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ if (class == CLASS_UNIVERSAL && tag == TAG_UTF8_STRING)
+ {
+ app->app_local->manufacturer_id = percent_data_escape (0, NULL,
+ p, objlen);
+ p += objlen;
+ n -= objlen;
+ /* Get next TLV. */
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || !objlen))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ app->app_local->token_label = percent_data_escape (0, NULL, p, objlen);
+
+ p += objlen;
+ n -= objlen;
+ /* Get next TLV. */
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || !objlen))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto leave;
+ }
+ /* The next is the mandatory tokenflags object. */
+ if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
+ {
+ app->app_local->tokenflagslen = objlen;
+ app->app_local->tokenflags = xtrymalloc (objlen);
+ if (!app->app_local->tokenflags)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (app->app_local->tokenflags, p, objlen);
+ p += objlen;
+ n -= objlen;
+ }
+
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Get all the basic information from the pkcs#15 card, check the
+ structure and initialize our local context. This is used once at
+ application initialization. */
+static gpg_error_t
+read_p15_info (app_t app)
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf;
+ unsigned int flag;
+
+ err = read_ef_tokeninfo (app);
+ if (err)
+ return err;
+ /* If we don't have a serial number yet but the TokenInfo provides
+ * one, use that. */
+ if (!APP_CARD(app)->serialno && app->app_local->serialno)
+ {
+ APP_CARD(app)->serialno = app->app_local->serialno;
+ APP_CARD(app)->serialnolen = app->app_local->serialnolen;
+ app->app_local->serialno = NULL;
+ app->app_local->serialnolen = 0;
+ err = app_munge_serialno (APP_CARD(app));
+ if (err)
+ return err;
+ }
+
+ release_lists (app);
+
+ if (IS_CARDOS_5 (app)
+ && app->app_local->manufacturer_id
+ && !ascii_strcasecmp (app->app_local->manufacturer_id, "GeNUA mbH"))
+ {
+ if (!app->app_local->card_product)
+ app->app_local->card_product = CARD_PRODUCT_GENUA;
+ }
+
+ /* Read the ODF so that we know the location of all directory
+ files. */
+ /* Fixme: We might need to get a non-standard ODF FID from TokenInfo. */
+ err = read_ef_odf (app, 0x5031);
+ if (err)
+ return err;
+
+ /* Read certificate information. */
+ log_assert (!app->app_local->certificate_info);
+ log_assert (!app->app_local->trusted_certificate_info);
+ log_assert (!app->app_local->useful_certificate_info);
+ err = read_ef_cdf (app, app->app_local->odf.certificates, 'c',
+ &app->app_local->certificate_info);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = read_ef_cdf (app, app->app_local->odf.trusted_certificates, 't',
+ &app->app_local->trusted_certificate_info);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = read_ef_cdf (app, app->app_local->odf.useful_certificates, 'u',
+ &app->app_local->useful_certificate_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+ if (err)
+ return err;
+
+ /* Read information about public keys. */
+ log_assert (!app->app_local->public_key_info);
+ err = read_ef_pukdf (app, app->app_local->odf.public_keys,
+ &app->app_local->public_key_info);
+ if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = read_ef_pukdf (app, app->app_local->odf.trusted_public_keys,
+ &app->app_local->public_key_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+ if (err)
+ return err;
+
+ /* Read information about private keys. */
+ log_assert (!app->app_local->private_key_info);
+ err = read_ef_prkdf (app, app->app_local->odf.private_keys,
+ &app->app_local->private_key_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+ if (err)
+ return err;
+
+ /* Read information about authentication objects. */
+ log_assert (!app->app_local->auth_object_info);
+ err = read_ef_aodf (app, app->app_local->odf.auth_objects,
+ &app->app_local->auth_object_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+
+
+ /* See whether we can extend the private key information using
+ * information from certificates. We use only the first matching
+ * certificate; if we want to change this strategy we should walk
+ * over the certificates and then find the corresponsing private key
+ * objects. */
+ app->app_local->any_gpgusage = 0;
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ cdf_object_t cdf;
+ char *extusage;
+ char *p, *pend;
+ int seen, i;
+
+ if (opt.debug)
+ log_printhex (prkdf->objid, prkdf->objidlen, "p15: prkdf id=");
+ if (cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf)
+ && cdf_object_from_label (app, prkdf->label, &cdf))
+ continue; /* No matching certificate. */
+ if (!cdf->cert) /* Read and parse the certificate. */
+ readcert_by_cdf (app, cdf, NULL, NULL);
+ if (!cdf->cert)
+ continue; /* Unsupported or broken certificate. */
+
+ if (prkdf->is_ecc)
+ {
+ const char *oid;
+ const unsigned char *der;
+ size_t off, derlen, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+
+ for (i=0; !(err = ksba_cert_get_extension
+ (cdf->cert, i, &oid, NULL, &off, &derlen)); i++)
+ if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.10") )
+ break;
+ if (!err && (der = ksba_cert_get_image (cdf->cert, NULL)))
+ {
+ der += off;
+ err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > derlen || tag != TAG_OCTET_STRING || ndef))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (!err)
+ {
+ derlen = objlen;
+ if (opt.debug)
+ log_printhex (der, derlen, "p15: OpenPGP KDF parms:");
+ /* Store them if they match the known OpenPGP format. */
+ if (derlen == 4 && der[0] == 3 && der[1] == 1)
+ memcpy (prkdf->ecdh_kdf, der, 4);
+ }
+ }
+ err = 0;
+ }
+
+ if (ksba_cert_get_ext_key_usages (cdf->cert, &extusage))
+ continue; /* No extended key usage attribute. */
+
+ if (opt.debug)
+ log_debug ("p15: ExtKeyUsages: %s\n", extusage);
+ p = extusage;
+ while (p && (pend=strchr (p, ':')))
+ {
+ *pend++ = 0;
+ if ( *pend == 'C' ) /* Look only at critical usages. */
+ {
+ prkdf->extusage.valid = 1;
+ seen = 1;
+ if (!strcmp (p, oid_kp_codeSigning)
+ || !strcmp (p, oid_kp_timeStamping)
+ || !strcmp (p, oid_kp_ocspSigning)
+ || !strcmp (p, oid_kp_ms_documentSigning)
+ || !strcmp (p, oid_kp_ms_old_documentSigning))
+ prkdf->extusage.sign = 1;
+ else if (!strcmp (p, oid_kp_emailProtection))
+ prkdf->extusage.encr = 1;
+ else if (!strcmp (p, oid_kp_serverAuth)
+ || !strcmp (p, oid_kp_clientAuth)
+ || !strcmp (p, oid_kp_ms_smartcardLogon))
+ prkdf->extusage.auth = 1;
+ else if (!strcmp (p, oid_kp_anyExtendedKeyUsage))
+ {
+ prkdf->extusage.sign = 1;
+ prkdf->extusage.encr = 1;
+ prkdf->extusage.auth = 1;
+ }
+ else
+ seen = 0;
+ }
+ else
+ seen = 0;
+
+ /* Now check the gpg Usage. Here we don't care about
+ * critical or non-critical here. */
+ if (seen)
+ ; /* No more need to look for other caps. */
+ else if (!strcmp (p, oid_kp_gpgUsageCert))
+ {
+ prkdf->gpgusage.cert = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageSign))
+ {
+ prkdf->gpgusage.sign = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageEncr))
+ {
+ prkdf->gpgusage.encr = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+ else if (!strcmp (p, oid_kp_gpgUsageAuth))
+ {
+ prkdf->gpgusage.auth = 1;
+ prkdf->gpgusage.any = 1;
+ app->app_local->any_gpgusage = 1;
+ }
+
+ /* Skip to next item. */
+ if ((p = strchr (pend, '\n')))
+ p++;
+ }
+ xfree (extusage);
+ }
+
+ /* See whether we can figure out something about the card. */
+ if (!app->app_local->card_product
+ && app->app_local->manufacturer_id
+ && !strcmp (app->app_local->manufacturer_id, "www.atos.net/cardos")
+ && IS_CARDOS_5 (app))
+ {
+ /* This is a modern CARDOS card. */
+ flag = 0;
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ if (prkdf->label && !strcmp (prkdf->label, "IdentityKey")
+ && prkdf->key_reference_valid && prkdf->key_reference == 1
+ && !prkdf->authid)
+ flag |= 1;
+ else if (prkdf->label && !strcmp (prkdf->label, "TransportKey")
+ && prkdf->key_reference_valid && prkdf->key_reference==2
+ && prkdf->authid)
+ flag |= 2;
+ }
+ if (flag == 3)
+ app->app_local->card_product = CARD_PRODUCT_RSCS;
+
+ }
+ if (!app->app_local->card_product
+ && app->app_local->token_label
+ && !strncmp (app->app_local->token_label, "D-TRUST Card V3", 15)
+ && app->app_local->card_type == CARD_TYPE_CARDOS_50)
+ {
+ app->app_local->card_product = CARD_PRODUCT_DTRUST;
+ }
+
+
+ /* Now print the info about the PrKDF. */
+ if (opt.verbose)
+ {
+ int i;
+ unsigned char *atr;
+ size_t atrlen;
+ const char *cardstr;
+
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ {
+ log_info ("p15: PrKDF %04hX: id=", app->app_local->odf.private_keys);
+ for (i=0; i < prkdf->objidlen; i++)
+ log_printf ("%02X", prkdf->objid[i]);
+ if (prkdf->label)
+ log_printf (" (%s)", prkdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < prkdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"",prkdf->path[i]);
+ if (prkdf->have_off)
+ log_printf ("[%lu/%lu]", prkdf->off, prkdf->len);
+ if (prkdf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < prkdf->authidlen; i++)
+ log_printf ("%02X", prkdf->authid[i]);
+ }
+ if (prkdf->key_reference_valid)
+ log_printf (" keyref=0x%02lX", prkdf->key_reference);
+ log_printf (" type=%s", prkdf->is_ecc? "ecc":"rsa");
+ if (prkdf->accessflags.any)
+ dump_keyaccess_flags (prkdf->accessflags);
+ dump_keyusage_flags (prkdf->usageflags);
+ if (prkdf->extusage.valid)
+ log_info ("p15: extusage=%s%s%s%s%s",
+ prkdf->extusage.sign? "sign":"",
+ (prkdf->extusage.sign
+ && prkdf->extusage.encr)?",":"",
+ prkdf->extusage.encr? "encr":"",
+ ((prkdf->extusage.sign || prkdf->extusage.encr)
+ && prkdf->extusage.auth)?",":"",
+ prkdf->extusage.auth? "auth":"");
+ if (prkdf->gpgusage.any)
+ dump_gpgusage_flags (prkdf->gpgusage);
+
+ log_printf ("\n");
+ }
+
+ log_info ("p15: TokenInfo:\n");
+ if (app->app_local->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (app->app_local->serialno, app->app_local->serialnolen,
+ "");
+ }
+ else if (APP_CARD(app)->serialno)
+ {
+ log_info ("p15: serialNumber .: ");
+ log_printhex (APP_CARD(app)->serialno, APP_CARD(app)->serialnolen,
+ "");
+ }
+
+ if (app->app_local->manufacturer_id)
+ log_info ("p15: manufacturerID: %s\n",
+ app->app_local->manufacturer_id);
+ if (app->app_local->card_product)
+ {
+ cardstr = cardproduct2str (app->app_local->card_product);
+ log_info ("p15: product ......: %d%s%s%s\n",
+ app->app_local->card_product,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
+ }
+ if (app->app_local->token_label)
+ log_info ("p15: label ........: %s\n", app->app_local->token_label);
+ if (app->app_local->tokenflags)
+ {
+ log_info ("p15: tokenflags ...:");
+ print_tokeninfo_tokenflags (app->app_local->tokenflags,
+ app->app_local->tokenflagslen);
+ log_printf ("\n");
+ }
+
+ log_info ("p15: atr ..........: ");
+ atr = apdu_get_atr (app_get_slot (app), &atrlen);
+ if (!atr)
+ log_printf ("[error]\n");
+ else
+ {
+ log_printhex (atr, atrlen, "");
+ xfree (atr);
+ }
+
+ cardstr = cardtype2str (app->app_local->card_type);
+ log_info ("p15: cardtype .....: %d%s%s%s\n",
+ app->app_local->card_type,
+ *cardstr? " (":"", cardstr, *cardstr? ")":"");
+ }
+
+ return err;
+}
+
+
+/* Helper to do_learn_status: Send information about all certificates
+ listed in CERTINFO back. Use CERTTYPE as type of the
+ certificate. */
+static gpg_error_t
+send_certinfo (app_t app, ctrl_t ctrl, const char *certtype,
+ cdf_object_t certinfo)
+{
+ for (; certinfo; certinfo = certinfo->next)
+ {
+ char *buf, *p;
+ const char *label;
+ char *labelbuf;
+
+ buf = xtrymalloc (9 + certinfo->objidlen*2 + 1);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ p = stpcpy (buf, "P15");
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
+ {
+ snprintf (p, 6, "-%04X",
+ (unsigned int)(app->app_local->home_df & 0xffff));
+ p += 5;
+ }
+ p = stpcpy (p, ".");
+ bin2hex (certinfo->objid, certinfo->objidlen, p);
+
+ label = (certinfo->label && *certinfo->label)? certinfo->label : "-";
+ labelbuf = percent_data_escape (0, NULL, label, strlen (label));
+ if (!labelbuf)
+ {
+ xfree (buf);
+ return gpg_error_from_syserror ();
+ }
+
+ send_status_info (ctrl, "CERTINFO",
+ certtype, strlen (certtype),
+ buf, strlen (buf),
+ labelbuf, strlen (labelbuf),
+ NULL, (size_t)0);
+ xfree (buf);
+ xfree (labelbuf);
+ }
+ return 0;
+}
+
+
+/* Get the keygrip of the private key object PRKDF. On success the
+ * keygrip, the algo and the length are stored in the KEYGRIP,
+ * KEYALGO, and KEYNBITS fields of the PRKDF object. */
+static gpg_error_t
+keygrip_from_prkdf (app_t app, prkdf_object_t prkdf)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+ unsigned char *der;
+ size_t derlen;
+ ksba_cert_t cert;
+ gcry_sexp_t s_pkey = NULL;
+
+ /* Easy if we got a cached version. */
+ if (prkdf->keygrip_valid)
+ return 0;
+
+ xfree (prkdf->common_name);
+ prkdf->common_name = NULL;
+ xfree (prkdf->serial_number);
+ prkdf->serial_number = NULL;
+
+ /* We could have also checked whether a public key directory file
+ * and a matching public key for PRKDF is available. This would
+ * make extraction of the key faster. However, this way we don't
+ * have a way to look at extended key attributes to check gpgusage.
+ * FIXME: Add public key lookup if no certificate was found. */
+
+ /* Look for a matching certificate. A certificate matches if the id
+ * matches the one of the private key info. If none was found we
+ * also try to match on the label. */
+ err = cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ err = cdf_object_from_label (app, prkdf->label, &cdf);
+ if (!err && !cdf)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (err)
+ goto leave;
+
+ err = readcert_by_cdf (app, cdf, &der, &derlen);
+ if (err)
+ goto leave;
+
+ err = ksba_cert_new (&cert);
+ if (!err)
+ err = ksba_cert_init_from_mem (cert, der, derlen);
+ xfree (der);
+ if (!err)
+ err = app_help_get_keygrip_string (cert, prkdf->keygrip, &s_pkey, NULL);
+ if (!err && !prkdf->gpgusage.any)
+ {
+ /* Try to get the CN and the SerialNumber from the certificate;
+ * we use a very simple approach here which should work in many
+ * cases. Eventually we should add a rfc-2253 parser into
+ * libksba to make it easier to parse such a string.
+ * We don't do this if this is marked as gpg key and thus
+ * has only a dummy certificate.
+ *
+ * First example string:
+ * "CN=Otto Schily,O=Miniluv,C=DE"
+ * Second example string:
+ * "2.5.4.5=#445452323030303236333531,2.5.4.4=#4B6F6368,"
+ * "2.5.4.42=#5765726E6572,CN=Werner Koch,OU=For testing"
+ * " purposes only!,O=Testorganisation,C=DE"
+ */
+ char *dn = ksba_cert_get_subject (cert, 0);
+ if (dn)
+ {
+ char *p, *pend, *buf;
+
+ p = strstr (dn, "CN=");
+ if (p && (p==dn || p[-1] == ','))
+ {
+ p += 3;
+ if (!(pend = strchr (p, ',')))
+ pend = p + strlen (p);
+ if (pend && pend > p
+ && (prkdf->common_name = xtrymalloc ((pend - p) + 1)))
+ {
+ memcpy (prkdf->common_name, p, pend-p);
+ prkdf->common_name[pend-p] = 0;
+ }
+ }
+ p = strstr (dn, "2.5.4.5=#"); /* OID of the SerialNumber */
+ if (p && (p==dn || p[-1] == ','))
+ {
+ p += 9;
+ if (!(pend = strchr (p, ',')))
+ pend = p + strlen (p);
+ if (pend && pend > p
+ && (buf = xtrymalloc ((pend - p) + 1)))
+ {
+ memcpy (buf, p, pend-p);
+ buf[pend-p] = 0;
+ if (!hex2str (buf, buf, strlen (buf)+1, NULL))
+ xfree (buf); /* Invalid hex encoding. */
+ else
+ prkdf->serial_number = buf;
+ }
+ }
+ ksba_free (dn);
+ }
+ }
+
+ if (!err && !prkdf->keytime)
+ {
+ ksba_isotime_t isot;
+ time_t t;
+
+ ksba_cert_get_validity (cert, 0, isot);
+ t = isotime2epoch (isot);
+ prkdf->keytime = (t == (time_t)(-1))? 0 : (u32)t;
+ prkdf->have_keytime = 1;
+ }
+
+ if (!err && !prkdf->keyalgostr)
+ prkdf->keyalgostr = pubkey_algo_string (s_pkey, NULL);
+
+ ksba_cert_release (cert);
+ if (err)
+ goto leave;
+
+ prkdf->keyalgo = get_pk_algo_from_key (s_pkey);
+ if (!prkdf->keyalgo)
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ prkdf->keynbits = gcry_pk_get_nbits (s_pkey);
+ if (!prkdf->keynbits)
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ prkdf->keygrip_valid = 1; /* Yeah, got everything. */
+
+ leave:
+ gcry_sexp_release (s_pkey);
+ return err;
+}
+
+
+/* Return a malloced keyref string for PRKDF. Returns NULL on
+ * malloc failure. */
+static char *
+keyref_from_prkdf (app_t app, prkdf_object_t prkdf)
+{
+ char *buf, *p;
+
+ buf = xtrymalloc (4 + 5 + prkdf->objidlen*2 + 1);
+ if (!buf)
+ return NULL;
+ p = stpcpy (buf, "P15");
+ if (app->app_local->home_df != DEFAULT_HOME_DF)
+ {
+ snprintf (p, 6, "-%04X",
+ (unsigned int)(app->app_local->home_df & 0xffff));
+ p += 5;
+ }
+ p = stpcpy (p, ".");
+ bin2hex (prkdf->objid, prkdf->objidlen, p);
+ return buf;
+}
+
+
+/* Helper to do_learn_status: Send information about all known
+ keypairs back. FIXME: much code duplication from
+ send_certinfo(). */
+static gpg_error_t
+send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t prkdf)
+{
+ gpg_error_t err;
+
+ for (; prkdf; prkdf = prkdf->next)
+ {
+ char *buf;
+ int j;
+
+ buf = keyref_from_prkdf (app, prkdf);
+ if (!buf)
+ return gpg_error_from_syserror ();
+
+ err = keygrip_from_prkdf (app, prkdf);
+ if (err)
+ {
+ log_error ("p15: error getting keygrip from ");
+ for (j=0; j < prkdf->pathlen; j++)
+ log_printf ("%s%04hX", j?"/":"", prkdf->path[j]);
+ log_printf (": %s\n", gpg_strerror (err));
+ }
+ else
+ {
+ char usage[5];
+ char keytime[20];
+ const char *algostr;
+ size_t usagelen = 0;
+
+ if (prkdf->gpgusage.any)
+ {
+ if (prkdf->gpgusage.sign)
+ usage[usagelen++] = 's';
+ if (prkdf->gpgusage.cert)
+ usage[usagelen++] = 'c';
+ if (prkdf->gpgusage.encr)
+ usage[usagelen++] = 'e';
+ if (prkdf->gpgusage.auth)
+ usage[usagelen++] = 'a';
+ }
+ else
+ {
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation)
+ && (!prkdf->extusage.valid
+ || prkdf->extusage.sign))
+ usage[usagelen++] = 's';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.sign))
+ usage[usagelen++] = 'c';
+ if ((prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap)
+ && (!prkdf->extusage.valid || prkdf->extusage.encr))
+ usage[usagelen++] = 'e';
+ if ((prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)
+ && (!prkdf->extusage.valid || prkdf->extusage.auth))
+ usage[usagelen++] = 'a';
+ }
+
+ log_assert (strlen (prkdf->keygrip) == 40);
+ if (prkdf->keytime && prkdf->have_keytime)
+ snprintf (keytime, sizeof keytime, "%lu",
+ (unsigned long)prkdf->keytime);
+ else
+ strcpy (keytime, "-");
+
+ algostr = prkdf->keyalgostr;
+
+ send_status_info (ctrl, "KEYPAIRINFO",
+ prkdf->keygrip, 2*KEYGRIP_LEN,
+ buf, strlen (buf),
+ usage, usagelen,
+ keytime, strlen (keytime),
+ algostr, strlen (algostr?algostr:""),
+ NULL, (size_t)0);
+ }
+ xfree (buf);
+ }
+ return 0;
+}
+
+
+
+/* This is the handler for the LEARN command. Note that if
+ * APP_LEARN_FLAG_REREAD is set and this function returns an error,
+ * the caller must deinitialize this application. */
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err;
+
+ if (flags & APP_LEARN_FLAG_REREAD)
+ {
+ err = read_p15_info (app);
+ if (err)
+ return err;
+ }
+
+ if ((flags & APP_LEARN_FLAG_KEYPAIRINFO))
+ err = 0;
+ else
+ {
+ err = do_getattr (app, ctrl, "MANUFACTURER");
+ if (!err)
+ err = send_certinfo (app, ctrl, "100",
+ app->app_local->certificate_info);
+ if (!err)
+ err = send_certinfo (app, ctrl, "101",
+ app->app_local->trusted_certificate_info);
+ if (!err)
+ err = send_certinfo (app, ctrl, "102",
+ app->app_local->useful_certificate_info);
+ }
+
+ if (!err)
+ err = send_keypairinfo (app, ctrl, app->app_local->private_key_info);
+
+ if (!err)
+ err = do_getattr (app, ctrl, "CHV-STATUS");
+ if (!err)
+ err = do_getattr (app, ctrl, "CHV-LABEL");
+
+
+ return err;
+}
+
+
+/* Read a certificate using the information in CDF and return the
+ * certificate in a newly malloced buffer R_CERT and its length
+ * R_CERTLEN. Also parses the certificate. R_CERT and R_CERTLEN may
+ * be NULL to do just the caching. */
+static gpg_error_t
+readcert_by_cdf (app_t app, cdf_object_t cdf,
+ unsigned char **r_cert, size_t *r_certlen)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ const unsigned char *p, *save_p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t totobjlen, objlen, hdrlen;
+ int rootca;
+ int i;
+
+ if (r_cert)
+ *r_cert = NULL;
+ if (r_certlen)
+ *r_certlen = 0;
+
+ /* First check whether it has been cached. */
+ if (cdf->cert)
+ {
+ const unsigned char *image;
+ size_t imagelen;
+
+ if (!r_cert || !r_certlen)
+ return 0; /* Caller does not actually want the result. */
+
+ image = ksba_cert_get_image (cdf->cert, &imagelen);
+ if (!image)
+ {
+ log_error ("p15: ksba_cert_get_image failed\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ *r_cert = xtrymalloc (imagelen);
+ if (!*r_cert)
+ return gpg_error_from_syserror ();
+ memcpy (*r_cert, image, imagelen);
+ *r_certlen = imagelen;
+ return 0;
+ }
+
+ if (DBG_CARD)
+ {
+ log_info ("p15: Reading CDF: id=");
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+ if (cdf->label)
+ log_printf (" (%s)", cdf->label);
+ log_info ("p15: path=");
+ for (i=0; i < cdf->pathlen; i++)
+ log_printf ("%s%04hX", i?"/":"", cdf->path[i]);
+ if (cdf->have_off)
+ log_printf ("[%lu/%lu]", cdf->off, cdf->len);
+ if (cdf->authid)
+ {
+ log_printf (" authid=");
+ for (i=0; i < cdf->authidlen; i++)
+ log_printf ("%02X", cdf->authid[i]);
+ }
+ log_printf ("\n");
+ }
+
+ /* Read the entire file. fixme: This could be optimized by first
+ reading the header to figure out how long the certificate
+ actually is. */
+ err = select_ef_by_path (app, cdf->path, cdf->pathlen);
+ if (err)
+ goto leave;
+
+ if (app->app_local->no_extended_mode || !cdf->len)
+ err = iso7816_read_binary_ext (app_get_slot (app), 0, cdf->off, 0,
+ &buffer, &buflen, NULL);
+ else
+ err = iso7816_read_binary_ext (app_get_slot (app), 1, cdf->off, cdf->len,
+ &buffer, &buflen, NULL);
+ if (!err && (!buflen || *buffer == 0xff))
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (err)
+ {
+ log_error ("p15: error reading certificate id=");
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+ log_printf (" at ");
+ for (i=0; i < cdf->pathlen; i++)
+ log_printf ("%s%04hX", i? "/":"", cdf->path[i]);
+ log_printf (": %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Check whether this is really a certificate. */
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed)
+ rootca = 0;
+ else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
+ rootca = 1;
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ totobjlen = objlen + hdrlen;
+ log_assert (totobjlen <= buflen);
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (!rootca
+ && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
+ {
+ /* The certificate seems to be contained in a userCertificate
+ container. Skip this and assume the following sequence is
+ the certificate. */
+ if (n < objlen)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ p += objlen;
+ n -= objlen;
+ save_p = p;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ totobjlen = objlen + hdrlen;
+ log_assert (save_p + totobjlen <= buffer + buflen);
+ memmove (buffer, save_p, totobjlen);
+ }
+
+
+ /* Try to parse and cache the certificate. */
+ err = ksba_cert_new (&cdf->cert);
+ if (!err)
+ {
+ err = ksba_cert_init_from_mem (cdf->cert, buffer, totobjlen);
+ if (!err) /* Call us to use the just cached cert object. */
+ err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
+ if (err)
+ {
+ ksba_cert_release (cdf->cert);
+ cdf->cert = NULL;
+ }
+
+ }
+ if (err)
+ {
+ log_error ("p15: caching certificate failed: %s\n",
+ gpg_strerror (err));
+ /* We return the certificate anyway so that the caller has a
+ * chance to get an even unsupported or broken certificate. */
+ if (r_cert && r_certlen)
+ {
+ *r_cert = buffer;
+ buffer = NULL;
+ *r_certlen = totobjlen;
+ }
+ }
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Handler for the READCERT command.
+
+ Read the certificate with id CERTID (as returned by learn_status in
+ the CERTINFO status lines) and return it in the freshly allocated
+ buffer to be stored at R_CERT and its length at R_CERTLEN. A error
+ code will be returned on failure and R_CERT and R_CERTLEN will be
+ set to (NULL,0). */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+ unsigned char **r_cert, size_t *r_certlen)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+
+ *r_cert = NULL;
+ *r_certlen = 0;
+ err = cdf_object_from_certid (app, certid, &cdf);
+ if (!err)
+ err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
+ return err;
+}
+
+
+/* Sort helper for an array of authentication objects. */
+static int
+compare_aodf_objid (const void *arg_a, const void *arg_b)
+{
+ const aodf_object_t a = *(const aodf_object_t *)arg_a;
+ const aodf_object_t b = *(const aodf_object_t *)arg_b;
+ int rc;
+
+ rc = memcmp (a->objid, b->objid,
+ a->objidlen < b->objidlen? a->objidlen : b->objidlen);
+ if (!rc)
+ {
+ if (a->objidlen < b->objidlen)
+ rc = -1;
+ else if (a->objidlen > b->objidlen)
+ rc = 1;
+ }
+ return rc;
+}
+
+
+static void
+send_key_fpr_line (ctrl_t ctrl, int number, const unsigned char *fpr)
+{
+ char buf[41];
+ char numbuf[25];
+
+ bin2hex (fpr, 20, buf);
+ if (number == -1)
+ *numbuf = 0; /* Don't print the key number */
+ else
+ snprintf (numbuf, sizeof numbuf, "%d", number);
+ send_status_info (ctrl, "KEY-FPR",
+ numbuf, (size_t)strlen(numbuf),
+ buf, (size_t)strlen (buf),
+ NULL, 0);
+}
+
+
+/* If possible Emit a FPR-KEY status line for the private key object
+ * PRKDF using NUMBER as index. */
+static void
+send_key_fpr (app_t app, ctrl_t ctrl, prkdf_object_t prkdf, int number)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+ unsigned char *pk, *fixed_pk;
+ size_t pklen, fixed_pklen;
+ const unsigned char *m, *e, *q;
+ size_t mlen, elen, qlen;
+ unsigned char fpr20[20];
+
+ if (cdf_object_from_objid (app, prkdf->objidlen, prkdf->objid, &cdf)
+ && cdf_object_from_label (app, prkdf->label, &cdf))
+ return;
+ if (!cdf->cert)
+ readcert_by_cdf (app, cdf, NULL, NULL);
+ if (!cdf->cert)
+ return;
+ if (!prkdf->have_keytime)
+ return;
+ pk = ksba_cert_get_public_key (cdf->cert);
+ if (!pk)
+ return;
+ pklen = gcry_sexp_canon_len (pk, 0, NULL, &err);
+
+ if (uncompress_ecc_q_in_canon_sexp (pk, pklen, &fixed_pk, &fixed_pklen))
+ {
+ xfree (pk);
+ return;
+ }
+ if (fixed_pk)
+ {
+ xfree (pk); pk = NULL;
+ pk = fixed_pk;
+ pklen = fixed_pklen;
+ }
+
+ switch (prkdf->keyalgo)
+ {
+ case GCRY_PK_RSA:
+ if (!get_rsa_pk_from_canon_sexp (pk, pklen,
+ &m, &mlen, &e, &elen)
+ && !compute_openpgp_fpr_rsa (4,
+ prkdf->keytime,
+ m, mlen, e, elen,
+ fpr20, NULL))
+ send_key_fpr_line (ctrl, number, fpr20);
+ break;
+
+ case GCRY_PK_ECC:
+ case GCRY_PK_ECDSA:
+ case GCRY_PK_ECDH:
+ case GCRY_PK_EDDSA:
+ /* Note that NUMBER 2 indicates the encryption key. */
+ if (!get_ecc_q_from_canon_sexp (pk, pklen, &q, &qlen)
+ && !compute_openpgp_fpr_ecc (4,
+ prkdf->keytime,
+ prkdf->keyalgostr,
+ number == 2,
+ q, qlen,
+ prkdf->ecdh_kdf, 4,
+ fpr20, NULL))
+ send_key_fpr_line (ctrl, number, fpr20);
+ break;
+
+ default: /* No Fingerprint for an unknown algo. */
+ break;
+
+ }
+ xfree (pk);
+}
+
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf;
+
+ if (!strcmp (name, "$AUTHKEYID")
+ || !strcmp (name, "$ENCRKEYID")
+ || !strcmp (name, "$SIGNKEYID"))
+ {
+ char *buf;
+
+ /* We return the ID of the first private key capable of the
+ * requested action. If any gpgusage flag has been set for the
+ * card we consult the gpgusage flags and not the regualr usage
+ * flags.
+ */
+ /* FIXME: This changed: Note that we do not yet return
+ * non_repudiation keys for $SIGNKEYID because our D-Trust
+ * testcard uses rsaPSS, which is not supported by gpgsm and not
+ * covered by the VS-NfD approval. */
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ if (app->app_local->any_gpgusage)
+ {
+ if ((name[1] == 'A' && prkdf->gpgusage.auth)
+ || (name[1] == 'E' && prkdf->gpgusage.encr)
+ || (name[1] == 'S' && prkdf->gpgusage.sign))
+ break;
+ }
+ else
+ {
+ if ((name[1] == 'A' && (prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover))
+ || (name[1] == 'E' && (prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap))
+ || (name[1] == 'S' && (prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover)))
+ break;
+ }
+ }
+ if (prkdf)
+ {
+ buf = keyref_from_prkdf (app, prkdf);
+ if (!buf)
+ return gpg_error_from_syserror ();
+
+ send_status_info (ctrl, name, buf, strlen (buf), NULL, 0);
+ xfree (buf);
+ }
+ return 0;
+ }
+ else if (!strcmp (name, "$DISPSERIALNO"))
+ {
+ /* For certain cards we return special IDs. There is no
+ general rule for it so we need to decide case by case. */
+ if (app->app_local->card_type == CARD_TYPE_BELPIC)
+ {
+ /* The eID card has a card number printed on the front matter
+ which seems to be a good indication. */
+ unsigned char *buffer;
+ const unsigned char *p;
+ size_t buflen, n;
+ unsigned short path[] = { 0x3F00, 0xDF01, 0x4031 };
+
+ err = select_ef_by_path (app, path, DIM(path) );
+ if (!err)
+ err = iso7816_read_binary (app_get_slot (app), 0, 0,
+ &buffer, &buflen);
+ if (err)
+ {
+ log_error ("p15: error accessing EF(ID): %s\n",
+ gpg_strerror (err));
+ return err;
+ }
+
+ p = find_tlv (buffer, buflen, 1, &n);
+ if (p && n == 12)
+ {
+ char tmp[12+2+1];
+ memcpy (tmp, p, 3);
+ tmp[3] = '-';
+ memcpy (tmp+4, p+3, 7);
+ tmp[11] = '-';
+ memcpy (tmp+12, p+10, 2);
+ tmp[14] = 0;
+ send_status_info (ctrl, name, tmp, strlen (tmp), NULL, 0);
+ xfree (buffer);
+ return 0;
+ }
+ xfree (buffer);
+ }
+ else
+ {
+ char *sn;
+
+ /* We use the first private key object which has a serial
+ * number set. If none was found, we parse the first
+ * object and see whether this has then a serial number. */
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ if (prkdf->serial_number)
+ break;
+ if (!prkdf && app->app_local->private_key_info)
+ {
+ prkdf = app->app_local->private_key_info;
+ keygrip_from_prkdf (app, prkdf);
+ if (!prkdf->serial_number)
+ prkdf = NULL;
+ }
+ sn = get_dispserialno (app, prkdf);
+ /* Unless there is a bogus S/N in the cert, or the product
+ * has a different strategy for the display-s/n, we should
+ * have a suitable one from the cert now. */
+ if (sn)
+ {
+ err = send_status_printf (ctrl, name, "%s", sn);
+ xfree (sn);
+ return err;
+ }
+ }
+ /* No abbreviated serial number. */
+ }
+ else if (!strcmp (name, "MANUFACTURER"))
+ {
+ if (app->app_local->manufacturer_id
+ && !strchr (app->app_local->manufacturer_id, '[')
+ && app->app_local->card_product)
+ return send_status_printf (ctrl, "MANUFACTURER", "0 %s [%s]",
+ app->app_local->manufacturer_id,
+ cardproduct2str (app->app_local->card_product));
+ else if (app->app_local->manufacturer_id)
+ return send_status_printf (ctrl, "MANUFACTURER", "0 %s",
+ app->app_local->manufacturer_id);
+ else
+ return 0;
+ }
+ else if (!strcmp (name, "CHV-STATUS") || !strcmp (name, "CHV-LABEL"))
+ {
+ int is_label = (name[4] == 'L');
+ aodf_object_t aodf;
+ aodf_object_t aodfarray[16];
+ int naodf = 0;
+ membuf_t mb;
+ char *p;
+ int i;
+
+ /* Put the AODFs into an array for easier sorting. Note that we
+ * handle onl the first 16 encountrer which should be more than
+ * enough. */
+ for (aodf = app->app_local->auth_object_info;
+ aodf && naodf < DIM(aodfarray); aodf = aodf->next)
+ if (aodf->objidlen && aodf->pin_reference_valid)
+ aodfarray[naodf++] = aodf;
+ qsort (aodfarray, naodf, sizeof *aodfarray, compare_aodf_objid);
+
+ init_membuf (&mb, 256);
+ for (i = 0; i < naodf; i++)
+ {
+ /* int j; */
+ /* log_debug ("p15: AODF[%d] pinref=%lu id=", */
+ /* i, aodfarray[i]->pin_reference); */
+ /* for (j=0; j < aodfarray[i]->objidlen; j++) */
+ /* log_printf ("%02X", aodfarray[i]->objid[j]); */
+ /* Note that there is no need to percent escape the label
+ * because all white space have been replaced by '_'. */
+ if (is_label)
+ put_membuf_printf (&mb, "%s%s", i? " ":"",
+ (aodfarray[i]->label
+ && *aodfarray[i]->label)?
+ aodfarray[i]->label:"X");
+ else
+ put_membuf_printf
+ (&mb, "%s%d", i? " ":"",
+ iso7816_verify_status (app_get_slot (app),
+ aodfarray[i]->pin_reference));
+ }
+ put_membuf( &mb, "", 1);
+ p = get_membuf (&mb, NULL);
+ if (!p)
+ return gpg_error_from_syserror ();
+ err = send_status_direct (ctrl, is_label? "CHV-LABEL":"CHV-STATUS", p);
+ xfree (p);
+ return err;
+ }
+ else if (!strcmp (name, "KEY-LABEL"))
+ {
+ /* Send KEY-LABEL lines for all private key objects. */
+ const char *label;
+ char *idbuf, *labelbuf;
+
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ idbuf = keyref_from_prkdf (app, prkdf);
+ if (!idbuf)
+ return gpg_error_from_syserror ();
+
+ label = (prkdf->label && *prkdf->label)? prkdf->label : "-";
+ labelbuf = percent_data_escape (0, NULL, label, strlen (label));
+ if (!labelbuf)
+ {
+ xfree (idbuf);
+ return gpg_error_from_syserror ();
+ }
+
+ send_status_info (ctrl, name,
+ idbuf, strlen (idbuf),
+ labelbuf, strlen(labelbuf),
+ NULL, 0);
+ xfree (idbuf);
+ xfree (labelbuf);
+ }
+ return 0;
+ }
+ else if (!strcmp (name, "KEY-FPR"))
+ {
+ /* Send KEY-FPR for the two openpgp keys. */
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ if (app->app_local->any_gpgusage)
+ {
+ if (prkdf->gpgusage.sign)
+ break;
+ }
+ else
+ {
+ if (prkdf->usageflags.sign || prkdf->usageflags.sign_recover)
+ break;
+ }
+ }
+ if (prkdf)
+ send_key_fpr (app, ctrl, prkdf, 1);
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ {
+ if (app->app_local->any_gpgusage)
+ {
+ if (prkdf->gpgusage.encr)
+ break;
+ }
+ else
+ {
+ if (prkdf->usageflags.decrypt || prkdf->usageflags.unwrap)
+ break;
+ }
+ }
+ if (prkdf)
+ send_key_fpr (app, ctrl, prkdf, 2);
+ return 0;
+ }
+
+ return gpg_error (GPG_ERR_INV_NAME);
+}
+
+
+
+
+/* Micardo cards require special treatment. This is a helper for the
+ crypto functions to manage the security environment. We expect that
+ the key file has already been selected. FID is the one of the
+ selected key. */
+static gpg_error_t
+micardo_mse (app_t app, unsigned short fid)
+{
+ gpg_error_t err;
+ int recno;
+ unsigned short refdata = 0;
+ int se_num;
+ unsigned char msebuf[10];
+
+ /* Read the KeyD file containing extra information on keys. */
+ err = iso7816_select_file (app_get_slot (app), 0x0013, 0);
+ if (err)
+ {
+ log_error ("p15: error reading EF_keyD: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ for (recno = 1, se_num = -1; ; recno++)
+ {
+ unsigned char *buffer;
+ size_t buflen;
+ size_t n, nn;
+ const unsigned char *p, *pp;
+
+ err = iso7816_read_record (app_get_slot (app), recno, 1, 0,
+ &buffer, &buflen);
+ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+ break; /* ready */
+ if (err)
+ {
+ log_error ("p15: error reading EF_keyD record: %s\n",
+ gpg_strerror (err));
+ return err;
+ }
+ if (opt.verbose)
+ {
+ log_info (buffer, buflen, "p15: keyD record: ");
+ log_printhex (buffer, buflen, "");
+ }
+ p = find_tlv (buffer, buflen, 0x83, &n);
+ if (p && n == 4 && ((p[2]<<8)|p[3]) == fid)
+ {
+ refdata = ((p[0]<<8)|p[1]);
+ /* Locate the SE DO and the there included sec env number. */
+ p = find_tlv (buffer, buflen, 0x7b, &n);
+ if (p && n)
+ {
+ pp = find_tlv (p, n, 0x80, &nn);
+ if (pp && nn == 1)
+ {
+ se_num = *pp;
+ xfree (buffer);
+ break; /* found. */
+ }
+ }
+ }
+ xfree (buffer);
+ }
+ if (se_num == -1)
+ {
+ log_error ("p15: CRT for keyfile %04hX not found\n", fid);
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ }
+
+
+ /* Restore the security environment to SE_NUM if needed */
+ if (se_num)
+ {
+ err = iso7816_manage_security_env (app_get_slot (app),
+ 0xf3, se_num, NULL, 0);
+ if (err)
+ {
+ log_error ("p15: restoring SE to %d failed: %s\n",
+ se_num, gpg_strerror (err));
+ return err;
+ }
+ }
+
+ /* Set the DST reference data. */
+ msebuf[0] = 0x83;
+ msebuf[1] = 0x03;
+ msebuf[2] = 0x80;
+ msebuf[3] = (refdata >> 8);
+ msebuf[4] = refdata;
+ err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xb6, msebuf, 5);
+ if (err)
+ {
+ log_error ("p15: setting SE to reference file %04hX failed: %s\n",
+ refdata, gpg_strerror (err));
+ return err;
+ }
+ return 0;
+}
+
+
+
+/* Prepare the verification of the PIN for the key PRKDF by checking
+ * the AODF and selecting the key file. KEYREF is used for error
+ * messages. AODF may be NULL if no verification needs to be done. */
+static gpg_error_t
+prepare_verify_pin (app_t app, const char *keyref,
+ prkdf_object_t prkdf, aodf_object_t aodf)
+{
+ gpg_error_t err;
+ int i;
+
+ if (aodf)
+ {
+ if (opt.verbose)
+ {
+ log_info ("p15: using AODF %04hX id=", aodf->fid);
+ for (i=0; i < aodf->objidlen; i++)
+ log_printf ("%02X", aodf->objid[i]);
+ log_printf ("\n");
+ }
+
+ if (aodf->authid && opt.verbose)
+ log_info ("p15: PIN is controlled by another authentication token\n");
+
+ if (aodf->pinflags.integrity_protected
+ || aodf->pinflags.confidentiality_protected)
+ {
+ log_error ("p15: PIN verification requires"
+ " unsupported protection method\n");
+ return gpg_error (GPG_ERR_BAD_PIN_METHOD);
+ }
+ if (!aodf->stored_length && aodf->pinflags.needs_padding)
+ {
+ log_error ("p15: PIN verification requires"
+ " padding but no length known\n");
+ return gpg_error (GPG_ERR_INV_CARD);
+ }
+ }
+
+
+ if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
+ {
+ /* According to our protocol analysis we need to select a
+ * special AID here. Before that the master file needs to be
+ * selected. (RID A000000167 is assigned to IBM) */
+ static char const dtrust_aid[] =
+ { 0xA0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4E };
+
+ err = iso7816_select_mf (app_get_slot (app));
+ if (!err)
+ err = iso7816_select_application (app_get_slot (app),
+ dtrust_aid, sizeof dtrust_aid, 0);
+ if (err)
+ log_error ("p15: error selecting D-TRUST's AID for key %s: %s\n",
+ keyref, gpg_strerror (err));
+ }
+ else if (prkdf)
+ {
+ /* Standard case: Select the key file. Note that this may
+ * change the security environment thus we need to do it before
+ * PIN verification. */
+ err = select_ef_by_path (app, prkdf->path, prkdf->pathlen);
+ if (err)
+ log_error ("p15: error selecting file for key %s: %s\n",
+ keyref, gpg_strerror (err));
+ }
+ else
+ {
+ log_info ("p15: skipping EF selection for auth object '%s'\n", keyref);
+ err = 0;
+ }
+
+ return err;
+}
+
+
+static int
+any_control_or_space (const char *string)
+{
+ const unsigned char *s;
+
+ for (s = string; *s; s++)
+ if (*s <= 0x20 || *s >= 0x7f)
+ return 1;
+ return 0;
+}
+
+static int
+any_control_or_space_mem (const void *buffer, size_t buflen)
+{
+ const unsigned char *s;
+
+ for (s = buffer; buflen; s++, buflen--)
+ if (*s <= 0x20 || *s >= 0x7f)
+ return 1;
+ return 0;
+}
+
+
+/* Return a malloced serial number to be shown to the user. PRKDF is
+ * used to get it from a certificate; PRKDF may be NULL. */
+static char *
+get_dispserialno (app_t app, prkdf_object_t prkdf)
+{
+ char *serial;
+ const unsigned char *s;
+ int i;
+ size_t n;
+
+ /* We prefer the SerialNumber RDN from the Subject-DN but we don't
+ * use it if it features a percent sign (special character in pin
+ * prompts) or has any control character. For some cards we use a
+ * different strategy. */
+ if (app->app_local->card_product == CARD_PRODUCT_RSCS)
+ {
+ /* We use only the right 8 hex digits. */
+ serial = app_get_serialno (app);
+ if (serial && (n=strlen (serial)) > 8)
+ memmove (serial, serial + n - 8, 9);
+ }
+ else if (IS_CARDOS_5 (app) && app->app_local->manufacturer_id
+ && !ascii_strcasecmp (app->app_local->manufacturer_id,
+ "Technology Nexus")
+ && app->serialno && app->serialnolen == 4+9
+ && !memcmp (app->serialno, "\xff\x00\x00\xff", 4)
+ && !any_control_or_space_mem (app->serialno + 4, 9))
+ {
+ /* Sample: ff0000ff354830313232363537 -> "5H01 2265 7" */
+ serial = xtrymalloc (9+2+1);
+ if (serial)
+ {
+ s = app->serialno + 4;
+ for (i=0; i < 4; i++)
+ serial[i] = *s++;
+ serial[i++] = ' ';
+ for (; i < 9; i++)
+ serial[i] = *s++;
+ serial[i++] = ' ';
+ serial[i++] = *s;
+ serial[i] = 0;
+ }
+ }
+ else if (prkdf && prkdf->serial_number && *prkdf->serial_number
+ && !strchr (prkdf->serial_number, '%')
+ && !any_control_or_space (prkdf->serial_number))
+ {
+ serial = xtrystrdup (prkdf->serial_number);
+ }
+ else
+ {
+ serial = app_get_serialno (app);
+ }
+
+ return serial;
+}
+
+
+/* Return an allocated string to be used as prompt. PRKDF may be
+ * NULL. Returns NULL on malloc error. */
+static char *
+make_pin_prompt (app_t app, int remaining, const char *firstline,
+ prkdf_object_t prkdf)
+{
+ char *serial, *tmpbuf, *result;
+ const char *holder = NULL;
+
+ serial = get_dispserialno (app, prkdf);
+
+ if (app->app_local->card_product == CARD_PRODUCT_GENUA)
+ {
+ /* The label of the first non SO-PIN is used for the holder. */
+ aodf_object_t aodf;
+
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->auth_type == AUTH_TYPE_PIN
+ && !aodf->pinflags.so_pin
+ && aodf->label)
+ {
+ holder = aodf->label;
+ break;
+ }
+ }
+
+ if (holder)
+ ;
+ else if (prkdf && prkdf->common_name)
+ holder = prkdf->common_name;
+ else if (app->app_local->token_label)
+ holder = app->app_local->token_label;
+ else
+ holder = "";
+
+ /* TRANSLATORS: Put a \x1f right before a colon. This can be
+ * used by pinentry to nicely align the names and values. Keep
+ * the %s at the start and end of the string. */
+ result = xtryasprintf (_("%s"
+ "Number\x1f: %s%%0A"
+ "Holder\x1f: %s"
+ "%s"),
+ "\x1e",
+ serial,
+ holder,
+ "");
+ xfree (serial);
+ if (!result)
+ return NULL; /* Out of core. */
+
+ /* Append a "remaining attempts" info if needed. */
+ if (remaining != -1 && remaining < 3)
+ {
+ char *rembuf;
+
+ /* TRANSLATORS: This is the number of remaining attempts to
+ * enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
+ rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
+ if (rembuf)
+ {
+ tmpbuf = strconcat (firstline, "%0A%0A", result,
+ "%0A%0A", rembuf, NULL);
+ xfree (rembuf);
+ }
+ else
+ tmpbuf = NULL;
+ xfree (result);
+ result = tmpbuf;
+ }
+ else
+ {
+ tmpbuf = strconcat (firstline, "%0A%0A", result, NULL);
+ xfree (result);
+ result = tmpbuf;
+ }
+
+ return result;
+}
+
+
+/* Given the private key object PRKDF and its authentication object
+ * AODF ask for the PIN and verify that PIN. If AODF is NULL, no
+ * authentication is done. */
+static gpg_error_t
+verify_pin (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **), void *pincb_arg,
+ prkdf_object_t prkdf, aodf_object_t aodf)
+{
+ gpg_error_t err;
+ char *pinvalue;
+ size_t pinvaluelen;
+ const char *label;
+ const char *errstr;
+ const char *s;
+ int remaining;
+ int pin_reference;
+ int verified = 0;
+ int i;
+
+ if (!aodf)
+ return 0;
+
+ pin_reference = aodf->pin_reference_valid? aodf->pin_reference : 0;
+
+ if (IS_CARDOS_5 (app))
+ {
+ /* We know that this card supports a verify status check. Note
+ * that in contrast to PIV cards ISO7816_VERIFY_NOT_NEEDED is
+ * not supported. We also don't use the pin_verified cache
+ * status because that is not as reliable as to ask the card
+ * about its state. */
+ if (prkdf) /* Clear the cache which we don't use. */
+ prkdf->pin_verified = 0;
+
+ remaining = iso7816_verify_status (app_get_slot (app), pin_reference);
+ if (remaining == ISO7816_VERIFY_NOT_NEEDED)
+ {
+ verified = 1;
+ remaining = -1;
+ }
+ else if (remaining < 0)
+ remaining = -1; /* We don't care about the concrete error. */
+ else if (remaining < 3)
+ log_info ("p15: PIN has %d attempts left\n", remaining);
+ }
+ else
+ remaining = -1; /* Unknown. */
+
+ /* Check whether we already verified it. */
+ if (prkdf && (prkdf->pin_verified || verified))
+ return 0; /* Already done. */
+
+ if (prkdf
+ && prkdf->usageflags.non_repudiation
+ && (app->app_local->card_type == CARD_TYPE_BELPIC
+ || app->app_local->card_product == CARD_PRODUCT_DTRUST))
+ label = _("||Please enter the PIN for the key to create "
+ "qualified signatures.");
+ else if (aodf->pinflags.so_pin)
+ label = _("|A|Please enter the Admin PIN");
+ else if (aodf->pinflags.unblocking_pin)
+ label = _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the standard keys.");
+ else
+ label = _("||Please enter the PIN for the standard keys.");
+
+ {
+ char *prompt = make_pin_prompt (app, remaining, label, prkdf);
+ if (!prompt)
+ err = gpg_error_from_syserror ();
+ else
+ err = pincb (pincb_arg, prompt, &pinvalue);
+ xfree (prompt);
+ }
+ if (err)
+ {
+ log_info ("p15: PIN callback returned error: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ /* We might need to cope with UTF8 things here. Not sure how
+ min_length etc. are exactly defined, for now we take them as
+ a plain octet count. */
+ if (strlen (pinvalue) < aodf->min_length)
+ {
+ log_error ("p15: PIN is too short; minimum length is %lu\n",
+ aodf->min_length);
+ err = gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else if (aodf->stored_length && strlen (pinvalue) > aodf->stored_length)
+ {
+ /* This would otherwise truncate the PIN silently. */
+ log_error ("p15: PIN is too large; maximum length is %lu\n",
+ aodf->stored_length);
+ err = gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else if (aodf->max_length_valid && strlen (pinvalue) > aodf->max_length)
+ {
+ log_error ("p15: PIN is too large; maximum length is %lu\n",
+ aodf->max_length);
+ err = gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ if (err)
+ {
+ xfree (pinvalue);
+ return err;
+ }
+
+ errstr = NULL;
+ err = 0;
+ switch (aodf->pintype)
+ {
+ case PIN_TYPE_BCD:
+ case PIN_TYPE_ASCII_NUMERIC:
+ for (s=pinvalue; digitp (s); s++)
+ ;
+ if (*s)
+ {
+ errstr = "Non-numeric digits found in PIN";
+ err = gpg_error (GPG_ERR_BAD_PIN);
+ }
+ break;
+ case PIN_TYPE_UTF8:
+ break;
+ case PIN_TYPE_HALF_NIBBLE_BCD:
+ errstr = "PIN type Half-Nibble-BCD is not supported";
+ break;
+ case PIN_TYPE_ISO9564_1:
+ errstr = "PIN type ISO9564-1 is not supported";
+ break;
+ default:
+ errstr = "Unknown PIN type";
+ break;
+ }
+ if (errstr)
+ {
+ log_error ("p15: can't verify PIN: %s\n", errstr);
+ xfree (pinvalue);
+ return err? err : gpg_error (GPG_ERR_BAD_PIN_METHOD);
+ }
+
+
+ if (aodf->pintype == PIN_TYPE_BCD )
+ {
+ char *paddedpin;
+ int ndigits;
+
+ for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
+ ;
+ paddedpin = xtrymalloc (aodf->stored_length+1);
+ if (!paddedpin)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (pinvalue);
+ return err;
+ }
+
+ i = 0;
+ paddedpin[i++] = 0x20 | (ndigits & 0x0f);
+ for (s=pinvalue; i < aodf->stored_length && *s && s[1]; s = s+2 )
+ paddedpin[i++] = (((*s - '0') << 4) | ((s[1] - '0') & 0x0f));
+ if (i < aodf->stored_length && *s)
+ paddedpin[i++] = (((*s - '0') << 4)
+ |((aodf->pad_char_valid?aodf->pad_char:0)&0x0f));
+
+ if (aodf->pinflags.needs_padding)
+ {
+ while (i < aodf->stored_length)
+ paddedpin[i++] = aodf->pad_char_valid? aodf->pad_char : 0;
+ }
+
+ xfree (pinvalue);
+ pinvalue = paddedpin;
+ pinvaluelen = i;
+ }
+ else if (aodf->pinflags.needs_padding)
+ {
+ char *paddedpin;
+
+ paddedpin = xtrymalloc (aodf->stored_length+1);
+ if (!paddedpin)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (pinvalue);
+ return err;
+ }
+ for (i=0, s=pinvalue; i < aodf->stored_length && *s; i++, s++)
+ paddedpin[i] = *s;
+ /* Not sure what padding char to use if none has been set.
+ For now we use 0x00; maybe a space would be better. */
+ for (; i < aodf->stored_length; i++)
+ paddedpin[i] = aodf->pad_char_valid? aodf->pad_char : 0;
+ paddedpin[i] = 0;
+ pinvaluelen = i;
+ xfree (pinvalue);
+ pinvalue = paddedpin;
+ }
+ else
+ pinvaluelen = strlen (pinvalue);
+
+ /* log_printhex (pinvalue, pinvaluelen, */
+ /* "about to verify with ref %lu pin:", pin_reference); */
+ err = iso7816_verify (app_get_slot (app), pin_reference,
+ pinvalue, pinvaluelen);
+ xfree (pinvalue);
+ if (err)
+ {
+ log_error ("p15: PIN verification failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+ if (opt.verbose)
+ log_info ("p15: PIN verification succeeded\n");
+ if (prkdf)
+ prkdf->pin_verified = 1;
+
+ return 0;
+}
+
+
+
+
+/* Handler for the PKSIGN command.
+
+ Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required, the PINCB will be used to ask for the PIN;
+ that callback should return the PIN in an allocated buffer and
+ store that as the 3rd argument. */
+static gpg_error_t
+do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf; /* The private key object. */
+ aodf_object_t aodf; /* The associated authentication object. */
+ int mse_done = 0; /* Set to true if the MSE has been done. */
+ unsigned int digestlen; /* Length of the hash. */
+ int exmode, le_value;
+ unsigned char oidbuf[64];
+ size_t oidbuflen;
+ size_t n;
+ unsigned char *indata_buffer = NULL; /* Malloced helper. */
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr || !indatalen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!(prkdf->usageflags.sign
+ || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation
+ || prkdf->gpgusage.cert
+ || prkdf->gpgusage.sign
+ || prkdf->gpgusage.auth ))
+ {
+ log_error ("p15: key %s may not be used for signing\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ if (!prkdf->authid)
+ {
+ log_error ("p15: no authentication object defined for %s\n", keyidstr);
+ /* fixme: we might want to go ahead and do without PIN
+ verification. */
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ }
+
+ /* Find the authentication object to this private key object. */
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->objidlen == prkdf->authidlen
+ && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
+ break;
+ if (!aodf)
+ log_info ("p15: no authentication for %s needed\n", keyidstr);
+
+ /* We need some more info about the key - get the keygrip to
+ * populate these fields. */
+ err = keygrip_from_prkdf (app, prkdf);
+ if (err)
+ {
+ log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+
+ digestlen = gcry_md_get_algo_dlen (hashalgo);
+
+ /* We handle ECC separately from RSA so that we do not need to touch
+ * working code. In particular we prepare the input data before the
+ * verify and a possible MSE. */
+ if (prkdf->is_ecc)
+ {
+ if (digestlen != 32 && digestlen != 48 && digestlen != 64)
+ {
+ log_error ("p15: ECC signing not possible: dlen=%u\n", digestlen);
+ err = gpg_error (GPG_ERR_DIGEST_ALGO);
+ goto leave;
+ }
+
+ if (indatalen == digestlen)
+ ; /* Already prepared. */
+ else if (indatalen > digestlen)
+ {
+ /* Assume a PKCS#1 prefix and remove it. */
+ oidbuflen = sizeof oidbuf;
+ err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen);
+ if (err)
+ {
+ log_error ("p15: no OID for hash algo %d\n", hashalgo);
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto leave;
+ }
+ if (indatalen != oidbuflen + digestlen
+ || memcmp (indata, oidbuf, oidbuflen))
+ {
+ log_error ("p15: input data too long for ECC: len=%zu\n",
+ indatalen);
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ indata = (const char*)indata + oidbuflen;
+ indatalen -= oidbuflen;
+ }
+ else
+ {
+ log_error ("p15: input data too short for ECC: len=%zu\n",
+ indatalen);
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ }
+ else /* Prepare RSA input. */
+ {
+ unsigned int framelen;
+ unsigned char *frame;
+ int i;
+
+ framelen = (prkdf->keynbits+7) / 8;
+ if (!framelen)
+ {
+ log_error ("p15: key length unknown"
+ " - can't prepare PKCS#v1.5 frame\n");
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ oidbuflen = sizeof oidbuf;
+ if (!hashalgo)
+ {
+ /* We assume that indata already has the required
+ * digestinfo; thus merely prepend the padding below. */
+ }
+ else if ((err = gcry_md_get_asnoid (hashalgo, &oidbuf, &oidbuflen)))
+ {
+ log_debug ("p15: no OID for hash algo %d\n", hashalgo);
+ goto leave;
+ }
+ else
+ {
+ if (indatalen == digestlen)
+ {
+ /* Plain hash in INDATA; prepend the digestinfo. */
+ indata_buffer = xtrymalloc (oidbuflen + indatalen);
+ if (!indata_buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (indata_buffer, oidbuf, oidbuflen);
+ memcpy (indata_buffer+oidbuflen, indata, indatalen);
+ indata = indata_buffer;
+ indatalen = oidbuflen + indatalen;
+ }
+ else if (indatalen == oidbuflen + digestlen
+ && !memcmp (indata, oidbuf, oidbuflen))
+ ; /* We already got the correct prefix. */
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ log_error ("p15: bad input for signing with RSA and hash %d\n",
+ hashalgo);
+ goto leave;
+ }
+ }
+ /* Now prepend the pkcs#v1.5 padding. We require at least 8
+ * byte of padding and 3 extra bytes for the prefix and the
+ * delimiting nul. */
+ if (!indatalen || indatalen + 8 + 4 > framelen)
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ log_error ("p15: input does not fit into a %u bit PKCS#v1.5 frame\n",
+ 8*framelen);
+ goto leave;
+ }
+ frame = xtrymalloc (framelen);
+ if (!frame)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (app->app_local->card_type == CARD_TYPE_BELPIC)
+ {
+ /* This card wants only the plain hash w/o any prefix. */
+ /* FIXME: We may want to remove this code because it is unlikely
+ * that such cards are still in use. */
+ memcpy (frame, indata, indatalen);
+ framelen = indatalen;
+ }
+ else
+ {
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 1; /* Block type. */
+ i = framelen - indatalen - 3 ;
+ memset (frame+n, 0xff, i);
+ n += i;
+ frame[n++] = 0; /* Delimiter. */
+ memcpy (frame+n, indata, indatalen);
+ n += indatalen;
+ log_assert (n == framelen);
+ }
+ /* And now put it into the indata_buffer. */
+ xfree (indata_buffer);
+ indata_buffer = frame;
+ indata = indata_buffer;
+ indatalen = framelen;
+ }
+
+ /* Prepare PIN verification. This is split so that we can do
+ * MSE operation for some task after having selected the key file but
+ * before sending the verify APDU. */
+ err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
+ if (err)
+ return err;
+
+ /* Due to the fact that the non-repudiation signature on a BELPIC
+ card requires a verify immediately before the DSO we set the
+ MSE before we do the verification. Other cards might also allow
+ this but I don't want to break anything, thus we do it only
+ for the BELPIC card here.
+ FIXME: see comment above about these cards. */
+ if (app->app_local->card_type == CARD_TYPE_BELPIC)
+ {
+ unsigned char mse[5];
+
+ mse[0] = 4; /* Length of the template. */
+ mse[1] = 0x80; /* Algorithm reference tag. */
+ if (hashalgo == MD_USER_TLS_MD5SHA1)
+ mse[2] = 0x01; /* Let card do pkcs#1 0xFF padding. */
+ else
+ mse[2] = 0x02; /* RSASSA-PKCS1-v1.5 using SHA1. */
+ mse[3] = 0x84; /* Private key reference tag. */
+ mse[4] = prkdf->key_reference_valid? prkdf->key_reference : 0x82;
+
+ err = iso7816_manage_security_env (app_get_slot (app),
+ 0x41, 0xB6,
+ mse, sizeof mse);
+ mse_done = 1;
+ }
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Now that we have all the information available run the actual PIN
+ * verification.*/
+ err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
+ if (err)
+ return err;
+
+ /* Manage security environment needs to be tweaked for certain cards. */
+ if (mse_done)
+ err = 0;
+ else if (app->app_local->card_type == CARD_TYPE_TCOS)
+ {
+ /* TCOS creates signatures always using the local key 0. MSE
+ may not be used. */
+ }
+ else if (app->app_local->card_type == CARD_TYPE_MICARDO)
+ {
+ if (!prkdf->pathlen)
+ err = gpg_error (GPG_ERR_BUG);
+ else
+ err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]);
+ }
+ else if (prkdf->key_reference_valid)
+ {
+ unsigned char mse[3];
+
+ mse[0] = 0x84; /* Select asym. key. */
+ mse[1] = 1;
+ mse[2] = prkdf->key_reference;
+
+ err = iso7816_manage_security_env (app_get_slot (app),
+ 0x41, 0xB6,
+ mse, sizeof mse);
+ }
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits >= 2048)
+ {
+ exmode = 1;
+ le_value = prkdf->keynbits / 8;
+ }
+ else
+ {
+ exmode = 0;
+ le_value = 0;
+ }
+
+ err = iso7816_compute_ds (app_get_slot (app),
+ exmode, indata, indatalen,
+ le_value, outdata, outdatalen);
+
+ leave:
+ xfree (indata_buffer);
+ return err;
+}
+
+
+/* Handler for the PKAUTH command.
+
+ This is basically the same as the PKSIGN command but we first check
+ that the requested key is suitable for authentication; that is, it
+ must match the criteria used for the attribute $AUTHKEYID. See
+ do_sign for calling conventions; there is no HASHALGO, though. */
+static gpg_error_t
+do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf;
+ int algo;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!(prkdf->usageflags.sign || prkdf->gpgusage.auth))
+ {
+ log_error ("p15: key %s may not be used for authentication\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
+ return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg,
+ indata, indatalen, outdata, outdatalen);
+}
+
+
+/* Handler for the PKDECRYPT command. Decrypt the data in INDATA and
+ * return the allocated result in OUTDATA. If a PIN is required the
+ * PINCB will be used to ask for the PIN; it should return the PIN in
+ * an allocated buffer and put it into PIN. */
+static gpg_error_t
+do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info)
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf; /* The private key object. */
+ aodf_object_t aodf; /* The associated authentication object. */
+ int exmode, le_value, padind;
+
+ (void)ctrl;
+ (void)r_info;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!indatalen || !indata || !outdatalen || !outdata)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!(prkdf->usageflags.decrypt
+ || prkdf->usageflags.unwrap
+ || prkdf->gpgusage.encr ))
+ {
+ log_error ("p15: key %s may not be used for decryption\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ /* Find the authentication object to this private key object. */
+ if (!prkdf->authid)
+ {
+ log_error ("p15: no authentication object defined for %s\n", keyidstr);
+ /* fixme: we might want to go ahead and do without PIN
+ verification. */
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ }
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->objidlen == prkdf->authidlen
+ && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
+ break;
+ if (!aodf)
+ log_info ("p15: no authentication for %s needed\n", keyidstr);
+
+ /* We need some more info about the key - get the keygrip to
+ * populate these fields. */
+ err = keygrip_from_prkdf (app, prkdf);
+ if (err)
+ {
+ log_error ("p15: keygrip_from_prkdf failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ /* Verify the PIN. */
+ err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
+ if (!err)
+ err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
+ if (err)
+ return err;
+
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+
+ err = iso7816_manage_security_env (app_get_slot (app), 0xF3, 0x01,
+ NULL, 0);
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+ }
+
+
+ /* The next is guess work for CardOS. */
+ if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
+ {
+ /* From analyzing an USB trace of a Windows signing application
+ * we see that the SE is simply reset to 0x14. It seems to be
+ * sufficient to do this for decryption; signing still works
+ * with the standard code despite that our trace showed that
+ * there the SE is restored to 0x09. Note that the special
+ * D-Trust AID is in any case select by prepare_verify_pin.
+ *
+ * Hey, D-Trust please hand over the specs so that you can
+ * actually sell your cards and we can properly implement it;
+ * other vendors understand this and do not demand ridiculous
+ * paper work or complicated procedures to get samples. */
+ err = iso7816_manage_security_env (app_get_slot (app),
+ 0xF3, 0x14, NULL, 0);
+
+ }
+ else if (prkdf->key_reference_valid)
+ {
+ unsigned char mse[9];
+ int i;
+
+ /* Note: This works with CardOS but the D-Trust card has the
+ * problem that the next created signature would be broken. */
+
+ i = 0;
+ if (!prkdf->is_ecc)
+ {
+ mse[i++] = 0x80; /* Algorithm reference. */
+ mse[i++] = 1;
+ mse[i++] = 0x0a; /* RSA, no padding. */
+ }
+ mse[i++] = 0x84; /* Key reference. */
+ mse[i++] = 1;
+ mse[i++] = prkdf->key_reference;
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ mse[i++] = 0x95; /* ???. */
+ mse[i++] = 1;
+ mse[i++] = 0x40;
+ }
+ log_assert (i <= DIM(mse));
+ err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
+ mse, i);
+ }
+ /* Check for MSE error. */
+ if (err)
+ {
+ log_error ("p15: MSE failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ exmode = le_value = 0;
+ padind = 0;
+ if (prkdf->keyalgo == GCRY_PK_RSA && prkdf->keynbits >= 2048)
+ {
+ exmode = 1; /* Extended length w/o a limit. */
+ le_value = prkdf->keynbits / 8;
+ }
+
+ if (app->app_local->card_product == CARD_PRODUCT_DTRUST)
+ padind = 0x81;
+
+ if (prkdf->is_ecc && IS_CARDOS_5(app))
+ {
+ if ((indatalen & 1) && *(const char *)indata == 0x04)
+ {
+ /* Strip indicator byte. */
+ indatalen--;
+ indata = (const char *)indata + 1;
+ }
+ err = iso7816_pso_csv (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value,
+ outdata, outdatalen);
+ }
+ else
+ {
+ err = iso7816_decipher (app_get_slot (app), exmode,
+ indata, indatalen,
+ le_value, padind,
+ outdata, outdatalen);
+ }
+
+ return err;
+}
+
+
+/* Perform a simple verify operation for the PIN specified by
+ * KEYIDSTR. Note that we require a key reference which is then used
+ * to select the authentication object. Return GPG_ERR_NO_PIN if a
+ * PIN is not required for using the private key KEYIDSTR. */
+static gpg_error_t
+do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf; /* The private key object. */
+ aodf_object_t aodf; /* The associated authentication object. */
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err
+ && gpg_err_code (err) != GPG_ERR_INV_ID
+ && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ return err;
+
+ if (err) /* Not found or invalid - assume it is the label. */
+ {
+ prkdf = NULL;
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->label && !ascii_strcasecmp (aodf->label, keyidstr))
+ break;
+ if (!aodf)
+ return err; /* Re-use the original error code. */
+ }
+ else /* Find the authentication object to this private key object. */
+ {
+ if (!prkdf->authid)
+ {
+ log_error ("p15: no authentication object defined for %s\n",
+ keyidstr);
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ }
+ for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next)
+ if (aodf->objidlen == prkdf->authidlen
+ && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen))
+ break;
+ if (!aodf) /* None found. */
+ return gpg_error (GPG_ERR_NO_PIN);
+ }
+
+ err = prepare_verify_pin (app, keyidstr, prkdf, aodf);
+ if (!err)
+ err = verify_pin (app, pincb, pincb_arg, prkdf, aodf);
+
+ return err;
+}
+
+
+/* Process the various keygrip based info requests. */
+static gpg_error_t
+do_with_keygrip (app_t app, ctrl_t ctrl, int action,
+ const char *want_keygripstr, int capability)
+{
+ gpg_error_t err;
+ char *serialno = NULL;
+ int as_data = 0;
+ prkdf_object_t prkdf;
+
+ /* First a quick check for valid parameters. */
+ switch (action)
+ {
+ case KEYGRIP_ACTION_LOOKUP:
+ if (!want_keygripstr)
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+ break;
+ case KEYGRIP_ACTION_SEND_DATA:
+ as_data = 1;
+ break;
+ case KEYGRIP_ACTION_WRITE_STATUS:
+ break;
+ default:
+ err = gpg_error (GPG_ERR_INV_ARG);
+ goto leave;
+ }
+
+ /* Allocate the s/n string if needed. */
+ if (action != KEYGRIP_ACTION_LOOKUP)
+ {
+ serialno = app_get_serialno (app);
+ if (!serialno)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ for (prkdf = app->app_local->private_key_info;
+ prkdf; prkdf = prkdf->next)
+ {
+ if (keygrip_from_prkdf (app, prkdf))
+ continue;
+
+ if (action == KEYGRIP_ACTION_LOOKUP)
+ {
+ if (!strcmp (prkdf->keygrip, want_keygripstr))
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ else if (!want_keygripstr || !strcmp (prkdf->keygrip, want_keygripstr))
+ {
+ char *keyref;
+
+ if (capability == GCRY_PK_USAGE_SIGN)
+ {
+ if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
+ || prkdf->usageflags.non_repudiation))
+ continue;
+ }
+ else if (capability == GCRY_PK_USAGE_ENCR)
+ {
+ if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
+ continue;
+ }
+ else if (capability == GCRY_PK_USAGE_AUTH)
+ {
+ if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover))
+ continue;
+ }
+
+ keyref = keyref_from_prkdf (app, prkdf);
+ if (!keyref)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ send_keyinfo (ctrl, as_data, prkdf->keygrip, serialno, keyref);
+ xfree (keyref);
+ if (want_keygripstr)
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ }
+
+ /* Return an error so that the dispatcher keeps on looping over the
+ * other applications. For clarity we use a different error code
+ * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR
+ * is not NULL. */
+ if (!want_keygripstr)
+ err = gpg_error (GPG_ERR_TRUE);
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+
+ leave:
+ xfree (serialno);
+ return err;
+}
+
+
+
+/* Assume that EF(DIR) has been selected. Read its content and figure
+ out the home EF of pkcs#15. Return that home DF or 0 if not found
+ and the value at the address of BELPIC indicates whether it was
+ found by the belpic aid. */
+static unsigned short
+read_home_df (int slot, int *r_belpic)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ const unsigned char *p, *pp;
+ size_t buflen, n, nn;
+ unsigned short result = 0;
+
+ *r_belpic = 0;
+
+ err = iso7816_read_binary (slot, 0, 0, &buffer, &buflen);
+ if (err)
+ {
+ log_error ("p15: error reading EF(DIR): %s\n", gpg_strerror (err));
+ return 0;
+ }
+
+ /* FIXME: We need to scan all records. */
+ p = find_tlv (buffer, buflen, 0x61, &n);
+ if (p && n)
+ {
+ pp = find_tlv (p, n, 0x4f, &nn);
+ if (pp && ((nn == sizeof pkcs15_aid && !memcmp (pp, pkcs15_aid, nn))
+ || (*r_belpic = (nn == sizeof pkcs15be_aid
+ && !memcmp (pp, pkcs15be_aid, nn)))))
+ {
+ pp = find_tlv (p, n, 0x50, &nn);
+ if (pp && opt.verbose)
+ log_info ("p15: application label from EF(DIR) is '%.*s'\n",
+ (int)nn, pp);
+ pp = find_tlv (p, n, 0x51, &nn);
+ if (pp && nn == 4 && *pp == 0x3f && !pp[1])
+ {
+ result = ((pp[2] << 8) | pp[3]);
+ if (opt.verbose)
+ log_info ("p15: application directory is 0x%04hX\n", result);
+ }
+ }
+ }
+ xfree (buffer);
+ return result;
+}
+
+
+/*
+ Select the PKCS#15 application on the card in SLOT.
+ */
+gpg_error_t
+app_select_p15 (app_t app)
+{
+ int slot = app_get_slot (app);
+ int rc;
+ unsigned short def_home_df = 0;
+ card_type_t card_type = CARD_TYPE_UNKNOWN;
+ int direct = 0;
+ int is_belpic = 0;
+ unsigned char *fci = NULL;
+ size_t fcilen;
+
+ rc = iso7816_select_application_ext (slot, pkcs15_aid, sizeof pkcs15_aid, 1,
+ &fci, &fcilen);
+ if (rc)
+ { /* Not found: Try to locate it from 2F00. We use direct path
+ selection here because it seems that the Belgian eID card
+ does only allow for that. Many other cards supports this
+ selection method too. Note, that we don't use
+ select_application above for the Belgian card - the call
+ works but it seems that it does not switch to the correct DF.
+ Using the 2f02 just works. */
+ unsigned short path[1] = { 0x2f00 };
+
+ rc = iso7816_select_path (slot, path, 1, 0);
+ if (!rc)
+ {
+ direct = 1;
+ def_home_df = read_home_df (slot, &is_belpic);
+ if (def_home_df)
+ {
+ path[0] = def_home_df;
+ rc = iso7816_select_path (slot, path, 1, 0);
+ }
+ }
+ }
+ if (rc)
+ { /* Still not found: Try the default DF. */
+ def_home_df = DEFAULT_HOME_DF;
+ rc = iso7816_select_file (slot, def_home_df, 1);
+ }
+ if (!rc)
+ {
+ /* Determine the type of the card. The general case is to look
+ it up from the ATR table. For the Belgian eID card we know
+ it instantly from the AID. */
+ if (is_belpic)
+ {
+ card_type = CARD_TYPE_BELPIC;
+ }
+ else
+ {
+ unsigned char *atr;
+ size_t atrlen;
+ int i;
+
+ atr = apdu_get_atr (app_get_slot (app), &atrlen);
+ if (!atr)
+ rc = gpg_error (GPG_ERR_INV_CARD);
+ else
+ {
+ for (i=0; card_atr_list[i].atrlen; i++)
+ if (card_atr_list[i].atrlen == atrlen
+ && !memcmp (card_atr_list[i].atr, atr, atrlen))
+ {
+ card_type = card_atr_list[i].type;
+ break;
+ }
+ xfree (atr);
+ }
+ }
+ }
+ if (!rc)
+ {
+ app->apptype = APPTYPE_P15;
+
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Set the home DF from the FCI returned by the select. */
+ if (!def_home_df && fci)
+ {
+ const unsigned char *s;
+ size_t n;
+
+ s = find_tlv (fci, fcilen, 0x83, &n);
+ if (s && n == 2)
+ def_home_df = buf16_to_ushort (s);
+ else
+ {
+ if (fcilen)
+ log_printhex (fci, fcilen, "fci:");
+ log_info ("p15: select did not return the DF - using default\n");
+ def_home_df = DEFAULT_HOME_DF;
+ }
+ }
+ app->app_local->home_df = def_home_df;
+
+ /* Store the card type. FIXME: We might want to put this into
+ the common APP structure. */
+ app->app_local->card_type = card_type;
+
+ app->app_local->card_product = CARD_PRODUCT_UNKNOWN;
+
+ /* Store whether we may and should use direct path selection. */
+ switch (card_type)
+ {
+ case CARD_TYPE_CARDOS_50:
+ case CARD_TYPE_CARDOS_53:
+ direct = 1;
+ break;
+ case CARD_TYPE_AET:
+ app->app_local->no_extended_mode = 1;
+ break;
+ default:
+ /* Use whatever has been determined above. */
+ break;
+ }
+ app->app_local->direct_path_selection = direct;
+
+ /* Read basic information and thus check whether this is a real
+ card. */
+ rc = read_p15_info (app);
+ if (rc)
+ goto leave;
+
+ /* Special serial number munging. We need to check for a German
+ prototype card right here because we need to access to
+ EF(TokenInfo). We mark such a serial number by the using a
+ prefix of FF0100. */
+ if (APP_CARD(app)->serialnolen == 12
+ && !memcmp (APP_CARD(app)->serialno,
+ "\xD2\x76\0\0\0\0\0\0\0\0\0\0", 12))
+ {
+ /* This is a German card with a silly serial number. Try to get
+ the serial number from the EF(TokenInfo). . */
+ unsigned char *p;
+
+ /* FIXME: actually get it from EF(TokenInfo). */
+
+ p = xtrymalloc (3 + APP_CARD(app)->serialnolen);
+ if (!p)
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ else
+ {
+ memcpy (p, "\xff\x01", 3);
+ memcpy (p+3, APP_CARD(app)->serialno, APP_CARD(app)->serialnolen);
+ APP_CARD(app)->serialnolen += 3;
+ xfree (APP_CARD(app)->serialno);
+ APP_CARD(app)->serialno = p;
+ }
+ }
+
+ app->fnc.deinit = do_deinit;
+ app->fnc.prep_reselect = NULL;
+ app->fnc.reselect = NULL;
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = do_readcert;
+ app->fnc.getattr = do_getattr;
+ app->fnc.setattr = NULL;
+ app->fnc.genkey = NULL;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = do_auth;
+ app->fnc.decipher = do_decipher;
+ app->fnc.change_pin = NULL;
+ app->fnc.check_pin = do_check_pin;
+ app->fnc.with_keygrip = do_with_keygrip;
+
+ leave:
+ if (rc)
+ do_deinit (app);
+ }
+
+ xfree (fci);
+ return rc;
+}
diff --git a/scd/app-sc-hsm.c b/scd/app-sc-hsm.c
new file mode 100644
index 0000000..1425b43
--- /dev/null
+++ b/scd/app-sc-hsm.c
@@ -0,0 +1,2087 @@
+/* app-sc-hsm.c - The SmartCard-HSM card application (www.smartcard-hsm.com).
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Andreas Schwier <andreas.schwier@cardcontact.de>
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ Code in this driver is based on app-p15.c with modifications.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "scdaemon.h"
+
+#include "iso7816.h"
+#include "../common/tlv.h"
+#include "apdu.h"
+
+
+/* The AID of the SmartCard-HSM applet. */
+static char const sc_hsm_aid[] = { 0xE8, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81,
+ 0xC3, 0x1F, 0x02, 0x01 };
+
+
+/* Special file identifier for SmartCard-HSM */
+typedef enum
+{
+ SC_HSM_PRKD_PREFIX = 0xC4,
+ SC_HSM_CD_PREFIX = 0xC8,
+ SC_HSM_DCOD_PREFIX = 0xC9,
+ SC_HSM_CA_PREFIX = 0xCA,
+ SC_HSM_KEY_PREFIX = 0xCC,
+ SC_HSM_EE_PREFIX = 0xCE
+} fid_prefix_type_t;
+
+
+/* The key types supported by the SmartCard-HSM */
+typedef enum
+ {
+ KEY_TYPE_RSA,
+ KEY_TYPE_ECC
+ } key_type_t;
+
+
+/* A bit array with for the key usage flags from the
+ commonKeyAttributes. */
+struct keyusage_flags_s
+{
+ unsigned int encrypt: 1;
+ unsigned int decrypt: 1;
+ unsigned int sign: 1;
+ unsigned int sign_recover: 1;
+ unsigned int wrap: 1;
+ unsigned int unwrap: 1;
+ unsigned int verify: 1;
+ unsigned int verify_recover: 1;
+ unsigned int derive: 1;
+ unsigned int non_repudiation: 1;
+};
+typedef struct keyusage_flags_s keyusage_flags_t;
+
+
+
+/* This is an object to store information about a Certificate
+ Directory File (CDF) in a format suitable for further processing by
+ us. To keep memory management, simple we use a linked list of
+ items; i.e. one such object represents one certificate and the list
+ the entire CDF. */
+struct cdf_object_s
+{
+ /* Link to next item when used in a linked list. */
+ struct cdf_object_s *next;
+
+ /* Length and allocated buffer with the Id of this object. */
+ size_t objidlen;
+ unsigned char *objid;
+
+ /* To avoid reading a certificate more than once, we cache it in an
+ allocated memory IMAGE of IMAGELEN. */
+ size_t imagelen;
+ unsigned char *image;
+
+ /* EF containing certificate */
+ unsigned short fid;
+};
+typedef struct cdf_object_s *cdf_object_t;
+
+
+
+/* This is an object to store information about a Private Key
+ Directory File (PrKDF) in a format suitable for further processing
+ by us. To keep memory management, simple we use a linked list of
+ items; i.e. one such object represents one certificate and the list
+ the entire PrKDF. */
+struct prkdf_object_s
+{
+ /* Link to next item when used in a linked list. */
+ struct prkdf_object_s *next;
+
+ /* Key type */
+ key_type_t keytype;
+
+ /* Key size in bits or 0 if unknown */
+ size_t keysize;
+
+ /* Length and allocated buffer with the Id of this object. */
+ size_t objidlen;
+ unsigned char *objid;
+
+ /* The key's usage flags. */
+ keyusage_flags_t usageflags;
+
+ /* The keyReference */
+ unsigned char key_reference;
+};
+typedef struct prkdf_object_s *prkdf_object_t;
+
+
+
+/* Context local to this application. */
+struct app_local_s
+{
+ /* Information on all certificates. */
+ cdf_object_t certificate_info;
+ /* Information on all trusted certificates. */
+ cdf_object_t trusted_certificate_info;
+ /* Information on all private keys. */
+ prkdf_object_t private_key_info;
+};
+
+
+
+/*** Local prototypes. ***/
+static gpg_error_t readcert_by_cdf (app_t app, cdf_object_t cdf,
+ unsigned char **r_cert, size_t *r_certlen);
+
+
+
+/* Release the CDF object A */
+static void
+release_cdflist (cdf_object_t a)
+{
+ while (a)
+ {
+ cdf_object_t tmp = a->next;
+ xfree (a->image);
+ xfree (a->objid);
+ xfree (a);
+ a = tmp;
+ }
+}
+
+
+
+/* Release the PrKDF object A. */
+static void
+release_prkdflist (prkdf_object_t a)
+{
+ while (a)
+ {
+ prkdf_object_t tmp = a->next;
+ xfree (a->objid);
+ xfree (a);
+ a = tmp;
+ }
+}
+
+
+
+/* Release all local resources. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ release_cdflist (app->app_local->certificate_info);
+ release_cdflist (app->app_local->trusted_certificate_info);
+ release_prkdflist (app->app_local->private_key_info);
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+
+/* Get the list of EFs from the SmartCard-HSM.
+ * On success a dynamically buffer containing the EF list is returned.
+ * The caller is responsible for freeing the buffer.
+ */
+static gpg_error_t
+list_ef (int slot, unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send_le (slot, 1, 0x80, 0x58, 0x00, 0x00, -1, NULL, 65536,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ return iso7816_map_sw (sw);
+}
+
+
+
+/* Do a select and a read for the file with EFID. EFID_DESC is a
+ description of the EF to be used with error messages. On success
+ BUFFER and BUFLEN contain the entire content of the EF. The caller
+ must free BUFFER only on success. */
+static gpg_error_t
+select_and_read_binary (int slot, unsigned short efid, const char *efid_desc,
+ unsigned char **buffer, size_t *buflen, int maxread)
+{
+ gpg_error_t err;
+ unsigned char cdata[4];
+ int sw;
+
+ cdata[0] = 0x54; /* Create ISO 7861-4 odd ins READ BINARY */
+ cdata[1] = 0x02;
+ cdata[2] = 0x00;
+ cdata[3] = 0x00;
+
+ sw = apdu_send_le(slot, 1, 0x00, 0xB1, efid >> 8, efid & 0xFF,
+ 4, cdata, maxread, buffer, buflen);
+
+ if (sw == SW_EOF_REACHED)
+ sw = SW_SUCCESS;
+
+ err = iso7816_map_sw (sw);
+ if (err)
+ {
+ log_error ("error reading %s (0x%04X): %s\n",
+ efid_desc, efid, gpg_strerror (err));
+ return err;
+ }
+ return 0;
+}
+
+
+
+/* Parse a cert Id string (or a key Id string) and return the binary
+ object Id string in a newly allocated buffer stored at R_OBJID and
+ R_OBJIDLEN. On Error NULL will be stored there and an error code
+ returned. On success caller needs to free the buffer at R_OBJID. */
+static gpg_error_t
+parse_certid (const char *certid, unsigned char **r_objid, size_t *r_objidlen)
+{
+ const char *s;
+ size_t objidlen;
+ unsigned char *objid;
+ int i;
+
+ *r_objid = NULL;
+ *r_objidlen = 0;
+
+ if (strncmp (certid, "HSM.", 4))
+ return gpg_error (GPG_ERR_INV_ID);
+ certid += 4;
+
+ for (s=certid, objidlen=0; hexdigitp (s); s++, objidlen++)
+ ;
+ if (*s || !objidlen || (objidlen%2))
+ return gpg_error (GPG_ERR_INV_ID);
+ objidlen /= 2;
+ objid = xtrymalloc (objidlen);
+ if (!objid)
+ return gpg_error_from_syserror ();
+ for (s=certid, i=0; i < objidlen; i++, s+=2)
+ objid[i] = xtoi_2 (s);
+ *r_objid = objid;
+ *r_objidlen = objidlen;
+ return 0;
+}
+
+
+
+/* Find a certificate object by the certificate ID CERTID and store a
+ pointer to it at R_CDF. */
+static gpg_error_t
+cdf_object_from_certid (app_t app, const char *certid, cdf_object_t *r_cdf)
+{
+ gpg_error_t err;
+ size_t objidlen;
+ unsigned char *objid;
+ cdf_object_t cdf;
+
+ err = parse_certid (certid, &objid, &objidlen);
+ if (err)
+ return err;
+
+ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
+ break;
+ if (!cdf)
+ for (cdf = app->app_local->trusted_certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == objidlen && !memcmp (cdf->objid, objid, objidlen))
+ break;
+ xfree (objid);
+ if (!cdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_cdf = cdf;
+ return 0;
+}
+
+
+
+/* Find a private key object by the key Id string KEYIDSTR and store a
+ pointer to it at R_PRKDF. */
+static gpg_error_t
+prkdf_object_from_keyidstr (app_t app, const char *keyidstr,
+ prkdf_object_t *r_prkdf)
+{
+ gpg_error_t err;
+ size_t objidlen;
+ unsigned char *objid;
+ prkdf_object_t prkdf;
+
+ err = parse_certid (keyidstr, &objid, &objidlen);
+ if (err)
+ return err;
+
+ for (prkdf = app->app_local->private_key_info; prkdf; prkdf = prkdf->next)
+ if (prkdf->objidlen == objidlen && !memcmp (prkdf->objid, objid, objidlen))
+ break;
+ xfree (objid);
+ if (!prkdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ *r_prkdf = prkdf;
+ return 0;
+}
+
+
+
+/* Parse the BIT STRING with the keyUsageFlags from the
+ CommonKeyAttributes. */
+static gpg_error_t
+parse_keyusage_flags (const unsigned char *der, size_t derlen,
+ keyusage_flags_t *usageflags)
+{
+ unsigned int bits, mask;
+ int i, unused, full;
+
+ memset (usageflags, 0, sizeof *usageflags);
+ if (!derlen)
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ unused = *der++; derlen--;
+ if ((!derlen && unused) || unused/8 > derlen)
+ return gpg_error (GPG_ERR_ENCODING_PROBLEM);
+ full = derlen - (unused+7)/8;
+ unused %= 8;
+ mask = 0;
+ for (i=1; unused; i <<= 1, unused--)
+ mask |= i;
+
+ /* First octet */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ mask = 0;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x80)) usageflags->encrypt = 1;
+ if ((bits & 0x40)) usageflags->decrypt = 1;
+ if ((bits & 0x20)) usageflags->sign = 1;
+ if ((bits & 0x10)) usageflags->sign_recover = 1;
+ if ((bits & 0x08)) usageflags->wrap = 1;
+ if ((bits & 0x04)) usageflags->unwrap = 1;
+ if ((bits & 0x02)) usageflags->verify = 1;
+ if ((bits & 0x01)) usageflags->verify_recover = 1;
+
+ /* Second octet. */
+ if (derlen)
+ {
+ bits = *der++; derlen--;
+ if (full)
+ full--;
+ else
+ {
+ bits &= ~mask;
+ }
+ }
+ else
+ bits = 0;
+ if ((bits & 0x80)) usageflags->derive = 1;
+ if ((bits & 0x40)) usageflags->non_repudiation = 1;
+
+ return 0;
+}
+
+
+
+/* Read and parse a Private Key Directory File containing a single key
+ description in PKCS#15 format. For each private key a matching
+ certificate description is created, if the certificate EF exists
+ and contains a X.509 certificate.
+
+ Example data:
+
+0000 30 2A 30 13 0C 11 4A 6F 65 20 44 6F 65 20 28 52 0*0...Joe Doe (R
+0010 53 41 32 30 34 38 29 30 07 04 01 01 03 02 02 74 SA2048)0.......t
+0020 A1 0A 30 08 30 02 04 00 02 02 08 00 ..0.0.......
+
+ Decoded example:
+
+SEQUENCE SIZE( 42 )
+ SEQUENCE SIZE( 19 )
+ UTF8-STRING SIZE( 17 ) -- label
+ 0000 4A 6F 65 20 44 6F 65 20 28 52 53 41 32 30 34 38 Joe Doe (RSA2048
+ 0010 29 )
+ SEQUENCE SIZE( 7 )
+ OCTET-STRING SIZE( 1 ) -- id
+ 0000 01
+ BIT-STRING SIZE( 2 ) -- key usage
+ 0000 02 74
+ A1 [ CONTEXT 1 ] IMPLICIT SEQUENCE SIZE( 10 )
+ SEQUENCE SIZE( 8 )
+ SEQUENCE SIZE( 2 )
+ OCTET-STRING SIZE( 0 ) -- empty path, req object in PKCS#15
+ INTEGER SIZE( 2 ) -- modulus size in bits
+ 0000 08 00
+*/
+static gpg_error_t
+read_ef_prkd (app_t app, unsigned short fid, prkdf_object_t *prkdresult,
+ cdf_object_t *cdresult)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ int i;
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ prkdf_object_t prkdf = NULL;
+ cdf_object_t cdf = NULL;
+ unsigned long ul;
+ const unsigned char *objid;
+ size_t objidlen;
+ keyusage_flags_t usageflags;
+ const char *s;
+ key_type_t keytype;
+ size_t keysize;
+
+ if (!fid)
+ return gpg_error (GPG_ERR_NO_DATA); /* No private keys. */
+
+ err = select_and_read_binary (app->slot, fid, "PrKDF", &buffer, &buflen, 255);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || (tag != TAG_SEQUENCE && tag != 0x00)))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ log_error ("error parsing PrKDF record: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ keytype = tag == 0x00 ? KEY_TYPE_ECC : KEY_TYPE_RSA;
+
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Parse the commonObjectAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Search the optional AuthId. We need to skip the optional Label
+ (UTF8STRING) and the optional CommonObjectFlags (BITSTRING). */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto no_authid;
+ if (err)
+ goto parse_error;
+
+ if (tag == TAG_UTF8_STRING)
+ {
+ ppp += objlen; /* Skip the Label. */
+ nnn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto no_authid;
+ if (err)
+ goto parse_error;
+ }
+ if (tag == TAG_BIT_STRING)
+ {
+ ppp += objlen; /* Skip the CommonObjectFlags. */
+ nnn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn || class != CLASS_UNIVERSAL))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto no_authid;
+ if (err)
+ goto parse_error;
+ }
+ if (tag == TAG_OCTET_STRING && objlen)
+ {
+ /* AuthId ignored */
+ }
+ no_authid:
+ ;
+ }
+
+ /* Parse the commonKeyAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Get the Id. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ objid = ppp;
+ objidlen = objlen;
+ ppp += objlen;
+ nnn -= objlen;
+
+ /* Get the KeyUsageFlags. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_BIT_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ err = parse_keyusage_flags (ppp, objlen, &usageflags);
+ if (err)
+ goto parse_error;
+
+ ppp += objlen;
+ nnn -= objlen;
+
+ /* Find the keyReference */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto leave_cki;
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ if (class == CLASS_UNIVERSAL && tag == TAG_BOOLEAN)
+ {
+ /* Skip the native element. */
+ ppp += objlen;
+ nnn -= objlen;
+
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto leave_cki;
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_BIT_STRING)
+ {
+ /* Skip the accessFlags. */
+ ppp += objlen;
+ nnn -= objlen;
+
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ goto leave_cki;
+ if (!err && objlen > nnn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ }
+ if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER)
+ {
+ /* Yep, this is the keyReference.
+ Note: UL is currently not used. */
+ for (ul=0; objlen; objlen--)
+ {
+ ul <<= 8;
+ ul |= (*ppp++) & 0xff;
+ nnn--;
+ }
+ }
+
+ leave_cki:
+ ;
+ }
+
+
+ /* Skip subClassAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class == CLASS_CONTEXT && tag == 0)
+ {
+ pp += objlen;
+ nn -= objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ }
+
+ /* Parse the keyAttributes. */
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ nn = objlen;
+
+ /* Check that the reference is a Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
+ {
+ errstr = "unsupported reference type";
+ goto parse_error;
+ }
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Parse the key size object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ keysize = 0;
+ if (class == CLASS_UNIVERSAL && tag == TAG_INTEGER && objlen == 2)
+ {
+ keysize = *pp++ << 8;
+ keysize += *pp++;
+ }
+
+ /* Create a new PrKDF list item. */
+ prkdf = xtrycalloc (1, sizeof *prkdf);
+ if (!prkdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ prkdf->keytype = keytype;
+ prkdf->keysize = keysize;
+ prkdf->objidlen = objidlen;
+ prkdf->objid = xtrymalloc (objidlen);
+ if (!prkdf->objid)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (prkdf);
+ prkdf = NULL;
+ goto leave;
+ }
+ memcpy (prkdf->objid, objid, objidlen);
+
+ prkdf->usageflags = usageflags;
+ prkdf->key_reference = fid & 0xFF;
+
+ log_debug ("PrKDF %04hX: id=", fid);
+ for (i=0; i < prkdf->objidlen; i++)
+ log_printf ("%02X", prkdf->objid[i]);
+ log_printf (" keyref=0x%02X", prkdf->key_reference);
+ log_printf (" keysize=%zu", prkdf->keysize);
+ log_printf (" usage=");
+ s = "";
+ if (prkdf->usageflags.encrypt)
+ {
+ log_printf ("%sencrypt", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.decrypt)
+ {
+ log_printf ("%sdecrypt", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.sign)
+ {
+ log_printf ("%ssign", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.sign_recover)
+ {
+ log_printf ("%ssign_recover", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.wrap )
+ {
+ log_printf ("%swrap", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.unwrap )
+ {
+ log_printf ("%sunwrap", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.verify )
+ {
+ log_printf ("%sverify", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.verify_recover)
+ {
+ log_printf ("%sverify_recover", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.derive )
+ {
+ log_printf ("%sderive", s);
+ s = ",";
+ }
+ if (prkdf->usageflags.non_repudiation)
+ {
+ log_printf ("%snon_repudiation", s);
+ }
+ log_printf ("\n");
+
+ xfree (buffer);
+ buffer = NULL;
+ buflen = 0;
+ err = select_and_read_binary (app->slot,
+ ((SC_HSM_EE_PREFIX << 8) | (fid & 0xFF)),
+ "CertEF", &buffer, &buflen, 1);
+ if (!err && buffer[0] == 0x30)
+ {
+ /* Create a matching CDF list item. */
+ cdf = xtrycalloc (1, sizeof *cdf);
+ if (!cdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ cdf->objidlen = prkdf->objidlen;
+ cdf->objid = xtrymalloc (cdf->objidlen);
+ if (!cdf->objid)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (cdf);
+ cdf = NULL;
+ goto leave;
+ }
+ memcpy (cdf->objid, prkdf->objid, objidlen);
+
+ cdf->fid = (SC_HSM_EE_PREFIX << 8) | (fid & 0xFF);
+
+ log_debug ("CDF %04hX: id=", fid);
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+ log_printf (" fid=%04X\n", cdf->fid);
+ }
+
+ goto leave; /* Ready. */
+
+ parse_error:
+ log_error ("error parsing PrKDF record (%d): %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ err = 0;
+
+ leave:
+ xfree (buffer);
+ if (err)
+ {
+ if (prkdf)
+ {
+ if (prkdf->objid)
+ xfree (prkdf->objid);
+ xfree (prkdf);
+ }
+ if (cdf)
+ {
+ if (cdf->objid)
+ xfree (cdf->objid);
+ xfree (cdf);
+ }
+ }
+ else
+ {
+ if (prkdf)
+ prkdf->next = *prkdresult;
+ *prkdresult = prkdf;
+ if (cdf)
+ {
+ cdf->next = *cdresult;
+ *cdresult = cdf;
+ }
+ }
+ return err;
+}
+
+
+
+/* Read and parse the Certificate Description File identified by FID.
+ On success a the CDF list gets stored at RESULT and the caller is
+ then responsible of releasing the object.
+
+ Example data:
+
+0000 30 35 30 11 0C 0B 43 65 72 74 69 66 69 63 61 74 050...Certificat
+0010 65 03 02 06 40 30 16 04 14 C2 01 7C 2F BA A4 4A e...@0.....|/..J
+0020 4A BB B8 49 11 DB 4A CA AA 7E 6A 2D 1B A1 08 30 J..I..J..~j-...0
+0030 06 30 04 04 02 CA 00 .0.....
+
+ Decoded example:
+
+SEQUENCE SIZE( 53 )
+ SEQUENCE SIZE( 17 )
+ UTF8-STRING SIZE( 11 ) -- label
+ 0000 43 65 72 74 69 66 69 63 61 74 65 Certificate
+ BIT-STRING SIZE( 2 ) -- common object attributes
+ 0000 06 40
+ SEQUENCE SIZE( 22 )
+ OCTET-STRING SIZE( 20 ) -- id
+ 0000 C2 01 7C 2F BA A4 4A 4A BB B8 49 11 DB 4A CA AA
+ 0010 7E 6A 2D 1B
+ A1 [ CONTEXT 1 ] IMPLICIT SEQUENCE SIZE( 8 )
+ SEQUENCE SIZE( 6 )
+ SEQUENCE SIZE( 4 )
+ OCTET-STRING SIZE( 2 ) -- path
+ 0000 CA 00 ..
+ */
+static gpg_error_t
+read_ef_cd (app_t app, unsigned short fid, cdf_object_t *result)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ size_t buflen;
+ const unsigned char *p;
+ size_t n, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ int i;
+ const unsigned char *pp;
+ size_t nn;
+ int where;
+ const char *errstr = NULL;
+ cdf_object_t cdf = NULL;
+ const unsigned char *objid;
+ size_t objidlen;
+
+ if (!fid)
+ return gpg_error (GPG_ERR_NO_DATA); /* No certificates. */
+
+ err = select_and_read_binary (app->slot, fid, "CDF", &buffer, &buflen, 255);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ log_error ("error parsing CDF record: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ pp = p;
+ nn = objlen;
+ p += objlen;
+ n -= objlen;
+
+ /* Skip the commonObjectAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ pp += objlen;
+ nn -= objlen;
+
+ /* Parse the commonCertificateAttributes. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ {
+ const unsigned char *ppp = pp;
+ size_t nnn = objlen;
+
+ pp += objlen;
+ nn -= objlen;
+
+ /* Get the Id. */
+ where = __LINE__;
+ err = parse_ber_header (&ppp, &nnn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nnn
+ || class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ objid = ppp;
+ objidlen = objlen;
+ }
+
+ /* Parse the certAttribute. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || class != CLASS_CONTEXT || tag != 1))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn
+ || class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ nn = objlen;
+
+ /* Check that the reference is a Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+ if (class != CLASS_UNIVERSAL || tag != TAG_SEQUENCE)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto parse_error;
+ }
+ nn = objlen;
+
+ /* Parse the Path object. */
+ where = __LINE__;
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && objlen > nn)
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ goto parse_error;
+
+ /* Make sure that the next element is a non zero path and of
+ even length (FID are two bytes each). */
+ if (class != CLASS_UNIVERSAL || tag != TAG_OCTET_STRING
+ || (objlen & 1) )
+ {
+ errstr = "invalid path reference";
+ goto parse_error;
+ }
+ /* Create a new CDF list item. */
+ cdf = xtrycalloc (1, sizeof *cdf);
+ if (!cdf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ cdf->objidlen = objidlen;
+ cdf->objid = xtrymalloc (objidlen);
+ if (!cdf->objid)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (cdf);
+ cdf = NULL;
+ goto leave;
+ }
+ memcpy (cdf->objid, objid, objidlen);
+
+ cdf->fid = (SC_HSM_CA_PREFIX << 8) | (fid & 0xFF);
+
+ log_debug ("CDF %04hX: id=", fid);
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+
+ goto leave;
+
+ parse_error:
+ log_error ("error parsing CDF record (%d): %s - skipped\n",
+ where, errstr? errstr : gpg_strerror (err));
+ err = 0;
+
+ leave:
+ xfree (buffer);
+ if (err)
+ {
+ if (cdf)
+ {
+ if (cdf->objid)
+ xfree (cdf->objid);
+ xfree (cdf);
+ }
+ }
+ else
+ {
+ if (cdf)
+ cdf->next = *result;
+ *result = cdf;
+ }
+ return err;
+}
+
+
+
+/* Read the device certificate and extract the serial number.
+
+ EF.C_DevAut (2F02) contains two CVCs, the first is the device
+ certificate, the second is the issuer certificate.
+
+ Example data:
+
+0000 7F 21 81 E2 7F 4E 81 9B 5F 29 01 00 42 0B 55 54 .!...N.._)..B.UT
+0010 43 43 30 32 30 30 30 30 32 7F 49 4F 06 0A 04 00 CC0200002.IO....
+0020 7F 00 07 02 02 02 02 03 86 41 04 6D FF D6 85 57 .........A.m...W
+0030 40 FB 10 5D 94 71 8A 94 D2 5E 50 33 E7 1E C0 6C @..].q...^P3...l
+0040 63 D5 C8 FC BA F3 02 1D 70 23 F6 47 E8 35 48 EF c.......p#.G.5H.
+0050 B5 94 72 3C 6F BE C0 EB 9A C7 FB 06 59 26 CF 65 ..r<o.......Y&.e
+0060 EF A1 72 E0 98 F3 F0 44 1B B7 71 5F 20 10 55 54 ..r....D..q_ .UT
+0070 43 43 30 32 30 30 30 31 33 30 30 30 30 30 7F 4C CC020001300000.L
+0080 10 06 0B 2B 06 01 04 01 81 C3 1F 03 01 01 53 01 ...+..........S.
+0090 00 5F 25 06 01 04 00 07 01 01 5F 24 06 02 01 00 ._%......._$....
+00A0 03 02 07 5F 37 40 7F 73 04 3B 06 63 79 41 BE 1A ..._7@.s.;.cyA..
+00B0 9F FC F6 77 67 2B 8A 41 D1 11 F6 9B 54 44 AD 19 ...wg+.A....TD..
+00C0 FB B8 0C C6 2F 34 71 8E 4F F6 92 59 34 61 D9 4F ..../4q.O..Y4a.O
+00D0 4A 86 36 A8 D8 9A C6 3C 17 7E 71 CE A8 26 D0 C5 J.6....<.~q..&..
+00E0 25 61 78 9D 01 F8 7F 21 81 E0 7F 4E 81 99 5F 29 %ax....!...N.._)
+00F0 01 00 42 0E 55 54 53 52 43 41 43 43 31 30 30 30 ..B.UTSRCACC1000
+0100 30 31 7F 49 4F 06 0A 04 00 7F 00 07 02 02 02 02 01.IO...........
+0110 03 86 41 04 2F EA 33 47 7F 45 81 E2 FC CB 66 87 ..A./.3G.E....f.
+0120 4B 96 21 1D 68 81 73 F2 9F 8F 6B 91 F0 DE 4B 54 K.!.h.s...k...KT
+0130 8E D8 F0 82 3D CB BE 10 98 A3 1E 4F F0 72 5C E5 ....=......O.r\.
+0140 7B 1E F7 3C 68 09 03 E8 A0 3F 3E 06 C1 B0 3C 18 {..<h....?>...<.
+0150 6B AC 06 EA 5F 20 0B 55 54 43 43 30 32 30 30 30 k..._ .UTCC02000
+0160 30 32 7F 4C 10 06 0B 2B 06 01 04 01 81 C3 1F 03 02.L...+........
+0170 01 01 53 01 80 5F 25 06 01 03 00 03 02 08 5F 24 ..S.._%......._$
+0180 06 02 01 00 03 02 07 5F 37 40 93 C1 42 8B B3 8E ......._7@..B...
+0190 42 61 6F 2C 19 E6 98 41 BD AA 60 BD E0 DD 4E F0 Bao,...A..`...N.
+01A0 15 D5 4F 71 B7 BB C3 3A F2 AD 27 5E DD EE 6D 12 ..Oq...:..'^..m.
+01B0 76 E6 2B A0 4C 01 CA C1 26 0C 45 6D C6 CB EC 92 v.+.L...&.Em....
+01C0 BF 38 18 AD 8F B2 29 40 A9 51 .8....)@.Q
+
+ The certificate format is defined in BSI TR-03110:
+
+7F21 [ APPLICATION 33 ] IMPLICIT SEQUENCE SIZE( 226 )
+ 7F4E [ APPLICATION 78 ] IMPLICIT SEQUENCE SIZE( 155 )
+ 5F29 [ APPLICATION 41 ] SIZE( 1 ) -- profile id
+ 0000 00
+ 42 [ APPLICATION 2 ] SIZE( 11 ) -- CAR
+ 0000 55 54 43 43 30 32 30 30 30 30 32 UTCC0200002
+ 7F49 [ APPLICATION 73 ] IMPLICIT SEQUENCE SIZE( 79 ) -- public key
+ OBJECT IDENTIFIER = { id-TA-ECDSA-SHA-256 }
+ 86 [ CONTEXT 6 ] SIZE( 65 )
+ 0000 04 6D FF D6 85 57 40 FB 10 5D 94 71 8A 94 D2 5E
+ 0010 50 33 E7 1E C0 6C 63 D5 C8 FC BA F3 02 1D 70 23
+ 0020 F6 47 E8 35 48 EF B5 94 72 3C 6F BE C0 EB 9A C7
+ 0030 FB 06 59 26 CF 65 EF A1 72 E0 98 F3 F0 44 1B B7
+ 0040 71
+ 5F20 [ APPLICATION 32 ] SIZE( 16 ) -- CHR
+ 0000 55 54 43 43 30 32 30 30 30 31 33 30 30 30 30 30 UTCC020001300000
+ 7F4C [ APPLICATION 76 ] IMPLICIT SEQUENCE SIZE( 16 ) -- CHAT
+ OBJECT IDENTIFIER = { 1 3 6 1 4 1 24991 3 1 1 }
+ 53 [ APPLICATION 19 ] SIZE( 1 )
+ 0000 00
+ 5F25 [ APPLICATION 37 ] SIZE( 6 ) -- Valid from
+ 0000 01 04 00 07 01 01
+ 5F24 [ APPLICATION 36 ] SIZE( 6 ) -- Valid to
+ 0000 02 01 00 03 02 07
+ 5F37 [ APPLICATION 55 ] SIZE( 64 ) -- Signature
+ 0000 7F 73 04 3B 06 63 79 41 BE 1A 9F FC F6 77 67 2B
+ 0010 8A 41 D1 11 F6 9B 54 44 AD 19 FB B8 0C C6 2F 34
+ 0020 71 8E 4F F6 92 59 34 61 D9 4F 4A 86 36 A8 D8 9A
+ 0030 C6 3C 17 7E 71 CE A8 26 D0 C5 25 61 78 9D 01 F8
+
+ The serial number is contained in tag 5F20, while the last 5 digits
+ are truncated.
+ */
+static gpg_error_t
+read_serialno(app_t app)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ size_t buflen;
+ const unsigned char *p,*chr;
+ size_t n, objlen, hdrlen, chrlen;
+ int class, tag, constructed, ndef;
+
+ err = select_and_read_binary (app->slot, 0x2F02, "EF.C_DevAut",
+ &buffer, &buflen, 512);
+ if (err)
+ return err;
+
+ p = buffer;
+ n = buflen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != 0x21))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if (err)
+ {
+ log_error ("error parsing C_DevAut: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ chr = find_tlv (p, objlen, 0x5F20, &chrlen);
+ if (!chr || chrlen <= 5)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ log_error ("CHR not found in CVC\n");
+ goto leave;
+ }
+ chrlen -= 5;
+
+ app->serialno = xtrymalloc (chrlen);
+ if (!app->serialno)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ app->serialnolen = chrlen;
+ memcpy (app->serialno, chr, chrlen);
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Get all the basic information from the SmartCard-HSM, check the
+ structure and initialize our local context. This is used once at
+ application initialization. */
+static gpg_error_t
+read_meta (app_t app)
+{
+ gpg_error_t err;
+ unsigned char *eflist = NULL;
+ size_t eflistlen = 0;
+ int i;
+
+ err = read_serialno(app);
+ if (err)
+ return err;
+
+ err = list_ef (app->slot, &eflist, &eflistlen);
+ if (err)
+ return err;
+
+ for (i = 0; i < eflistlen; i += 2)
+ {
+ switch(eflist[i])
+ {
+ case SC_HSM_KEY_PREFIX:
+ if (eflist[i + 1] == 0) /* No key with ID=0 */
+ break;
+ err = read_ef_prkd (app, ((SC_HSM_PRKD_PREFIX << 8) | eflist[i + 1]),
+ &app->app_local->private_key_info,
+ &app->app_local->certificate_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+ if (err)
+ return err;
+ break;
+ case SC_HSM_CD_PREFIX:
+ err = read_ef_cd (app, ((eflist[i] << 8) | eflist[i + 1]),
+ &app->app_local->trusted_certificate_info);
+ if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+ err = 0;
+ if (err)
+ return err;
+ break;
+ }
+ }
+
+ xfree (eflist);
+
+ return err;
+}
+
+
+
+/* Helper to do_learn_status: Send information about all certificates
+ listed in CERTINFO back. Use CERTTYPE as type of the
+ certificate. */
+static gpg_error_t
+send_certinfo (ctrl_t ctrl, const char *certtype, cdf_object_t certinfo)
+{
+ for (; certinfo; certinfo = certinfo->next)
+ {
+ char *buf, *p;
+
+ buf = xtrymalloc (4 + certinfo->objidlen*2 + 1);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ p = stpcpy (buf, "HSM.");
+ bin2hex (certinfo->objid, certinfo->objidlen, p);
+
+ send_status_info (ctrl, "CERTINFO",
+ certtype, strlen (certtype),
+ buf, strlen (buf),
+ NULL, (size_t)0);
+ xfree (buf);
+ }
+ return 0;
+}
+
+
+
+/* Get the keygrip of the private key object PRKDF. On success the
+ keygrip gets returned in the caller provided 41 byte buffer
+ R_GRIPSTR. */
+static gpg_error_t
+keygripstr_from_prkdf (app_t app, prkdf_object_t prkdf, char *r_gripstr)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+ unsigned char *der;
+ size_t derlen;
+ ksba_cert_t cert;
+
+ /* Look for a matching certificate. A certificate matches if the Id
+ matches the one of the private key info. */
+ for (cdf = app->app_local->certificate_info; cdf; cdf = cdf->next)
+ if (cdf->objidlen == prkdf->objidlen
+ && !memcmp (cdf->objid, prkdf->objid, prkdf->objidlen))
+ break;
+ if (!cdf)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ err = readcert_by_cdf (app, cdf, &der, &derlen);
+ if (err)
+ return err;
+
+ err = ksba_cert_new (&cert);
+ if (!err)
+ err = ksba_cert_init_from_mem (cert, der, derlen);
+ xfree (der);
+ if (!err)
+ err = app_help_get_keygrip_string (cert, r_gripstr, NULL, NULL);
+ ksba_cert_release (cert);
+
+ return err;
+}
+
+
+
+/* Helper to do_learn_status: Send information about all known
+ keypairs back. */
+static gpg_error_t
+send_keypairinfo (app_t app, ctrl_t ctrl, prkdf_object_t keyinfo)
+{
+ gpg_error_t err;
+
+ for (; keyinfo; keyinfo = keyinfo->next)
+ {
+ char gripstr[40+1];
+ char *buf, *p;
+
+ buf = xtrymalloc (4 + keyinfo->objidlen*2 + 1);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ p = stpcpy (buf, "HSM.");
+ bin2hex (keyinfo->objid, keyinfo->objidlen, p);
+
+ err = keygripstr_from_prkdf (app, keyinfo, gripstr);
+ if (err)
+ {
+ log_error ("can't get keygrip from %04X\n", keyinfo->key_reference);
+ }
+ else
+ {
+ assert (strlen (gripstr) == 40);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ gripstr, 40,
+ buf, strlen (buf),
+ NULL, (size_t)0);
+ }
+ xfree (buf);
+ }
+ return 0;
+}
+
+
+
+/* This is the handler for the LEARN command. */
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err;
+
+ if ((flags & 1))
+ err = 0;
+ else
+ {
+ err = send_certinfo (ctrl, "100", app->app_local->certificate_info);
+ if (!err)
+ err = send_certinfo (ctrl, "101",
+ app->app_local->trusted_certificate_info);
+ }
+
+ if (!err)
+ err = send_keypairinfo (app, ctrl, app->app_local->private_key_info);
+
+ return err;
+}
+
+
+
+/* Read a certificate using the information in CDF and return the
+ certificate in a newly allocated buffer R_CERT and its length
+ R_CERTLEN. */
+static gpg_error_t
+readcert_by_cdf (app_t app, cdf_object_t cdf,
+ unsigned char **r_cert, size_t *r_certlen)
+{
+ gpg_error_t err;
+ unsigned char *buffer = NULL;
+ const unsigned char *p, *save_p;
+ size_t buflen, n;
+ int class, tag, constructed, ndef;
+ size_t totobjlen, objlen, hdrlen;
+ int rootca;
+ int i;
+
+ *r_cert = NULL;
+ *r_certlen = 0;
+
+ /* First check whether it has been cached. */
+ if (cdf->image)
+ {
+ *r_cert = xtrymalloc (cdf->imagelen);
+ if (!*r_cert)
+ return gpg_error_from_syserror ();
+ memcpy (*r_cert, cdf->image, cdf->imagelen);
+ *r_certlen = cdf->imagelen;
+ return 0;
+ }
+
+ err = select_and_read_binary (app->slot, cdf->fid, "CD",
+ &buffer, &buflen, 4096);
+ if (err)
+ {
+ log_error ("error reading certificate with Id ");
+ for (i=0; i < cdf->objidlen; i++)
+ log_printf ("%02X", cdf->objid[i]);
+ log_printf (": %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Check whether this is really a certificate. */
+ p = buffer;
+ n = buflen;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed)
+ rootca = 0;
+ else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
+ rootca = 1;
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ totobjlen = objlen + hdrlen;
+ assert (totobjlen <= buflen);
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+
+ if (!rootca
+ && class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
+ {
+ /* The certificate seems to be contained in a userCertificate
+ container. Skip this and assume the following sequence is
+ the certificate. */
+ if (n < objlen)
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ p += objlen;
+ n -= objlen;
+ save_p = p;
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (err)
+ goto leave;
+ if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
+ {
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ goto leave;
+ }
+ totobjlen = objlen + hdrlen;
+ assert (save_p + totobjlen <= buffer + buflen);
+ memmove (buffer, save_p, totobjlen);
+ }
+
+ *r_cert = buffer;
+ buffer = NULL;
+ *r_certlen = totobjlen;
+
+ /* Try to cache it. */
+ if (!cdf->image && (cdf->image = xtrymalloc (*r_certlen)))
+ {
+ memcpy (cdf->image, *r_cert, *r_certlen);
+ cdf->imagelen = *r_certlen;
+ }
+
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+
+/* Handler for the READCERT command.
+
+ Read the certificate with id CERTID (as returned by learn_status in
+ the CERTINFO status lines) and return it in the freshly allocated
+ buffer to be stored at R_CERT and its length at R_CERTLEN. A error
+ code will be returned on failure and R_CERT and R_CERTLEN will be
+ set to (NULL,0). */
+static gpg_error_t
+do_readcert (app_t app, const char *certid,
+ unsigned char **r_cert, size_t *r_certlen)
+{
+ gpg_error_t err;
+ cdf_object_t cdf;
+
+ *r_cert = NULL;
+ *r_certlen = 0;
+ err = cdf_object_from_certid (app, certid, &cdf);
+ if (!err)
+ err = readcert_by_cdf (app, cdf, r_cert, r_certlen);
+ return err;
+}
+
+
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ if (!strcmp (name, "$AUTHKEYID"))
+ {
+ char *buf, *p;
+ prkdf_object_t prkdf;
+
+ /* We return the ID of the first private key capable of
+ signing. */
+ for (prkdf = app->app_local->private_key_info; prkdf;
+ prkdf = prkdf->next)
+ if (prkdf->usageflags.sign)
+ break;
+ if (prkdf)
+ {
+ buf = xtrymalloc (4 + prkdf->objidlen*2 + 1);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ p = stpcpy (buf, "HSM.");
+ bin2hex (prkdf->objid, prkdf->objidlen, p);
+
+ send_status_info (ctrl, name, buf, strlen (buf), NULL, 0);
+ xfree (buf);
+ return 0;
+ }
+ }
+ else if (!strcmp (name, "$DISPSERIALNO"))
+ {
+ send_status_info (ctrl, name, app->serialno, app->serialnolen, NULL, 0);
+ return 0;
+ }
+
+ return gpg_error (GPG_ERR_INV_NAME);
+}
+
+
+
+/* Apply PKCS#1 V1.5 padding for signature operation. The function
+ * combines padding, digest info and the hash value. The buffer must
+ * be allocated by the caller matching the key size. */
+static void
+apply_PKCS_padding(const unsigned char *dig, int diglen,
+ const unsigned char *prefix, int prefixlen,
+ unsigned char *buff, int bufflen)
+{
+ int i, n_ff;
+
+ /* Caller must ensure a sufficient buffer. */
+ if (diglen + prefixlen + 4 > bufflen)
+ return;
+ n_ff = bufflen - diglen - prefixlen - 3;
+
+ *buff++ = 0x00;
+ *buff++ = 0x01;
+ for (i=0; i < n_ff; i++)
+ *buff++ = 0xFF;
+ *buff++ = 0x00;
+
+ if (prefix)
+ memcpy (buff, prefix, prefixlen);
+ buff += prefixlen;
+ memcpy (buff, dig, diglen);
+}
+
+
+
+/* Decode a digest info structure (DI,DILEN) to extract the hash
+ * value. The buffer HASH to receive the digest must be provided by
+ * the caller with HASHLEN pointing to the inbound length. HASHLEN is
+ * updated to the outbound length. */
+static int
+hash_from_digestinfo (const unsigned char *di, size_t dilen,
+ unsigned char *hash, size_t *hashlen)
+{
+ const unsigned char *p,*pp;
+ size_t n, nn, objlen, hdrlen;
+ int class, tag, constructed, ndef;
+ gpg_error_t err;
+
+ p = di;
+ n = dilen;
+
+ err = parse_ber_header (&p, &n, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > n || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if ( err )
+ return err;
+
+ pp = p;
+ nn = objlen;
+
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_SEQUENCE))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if ( err )
+ return err;
+
+ pp += objlen;
+ nn -= objlen;
+
+ err = parse_ber_header (&pp, &nn, &class, &tag, &constructed,
+ &ndef, &objlen, &hdrlen);
+ if (!err && (objlen > nn || tag != TAG_OCTET_STRING))
+ err = gpg_error (GPG_ERR_INV_OBJ);
+ if ( err )
+ return err;
+
+ if (*hashlen < objlen)
+ return gpg_error (GPG_ERR_TOO_SHORT);
+ memcpy (hash, pp, objlen);
+ *hashlen = objlen;
+ return 0;
+}
+
+
+/* Perform PIN verification
+ */
+static gpg_error_t
+verify_pin (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+ pininfo_t pininfo;
+ char *pinvalue;
+ char *prompt;
+ int sw;
+
+ sw = apdu_send_simple (app->slot, 0, 0x00, ISO7816_VERIFY, 0x00, 0x81,
+ -1, NULL);
+
+ if (sw == SW_SUCCESS)
+ return 0; /* PIN already verified */
+
+ if (sw == SW_REF_DATA_INV)
+ {
+ log_error ("SmartCard-HSM not initialized. Run sc-hsm-tool first\n");
+ return gpg_error (GPG_ERR_NO_PIN);
+ }
+
+ if (sw == SW_CHV_BLOCKED)
+ {
+ log_error ("PIN Blocked\n");
+ return gpg_error (GPG_ERR_PIN_BLOCKED);
+ }
+
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = 0;
+ pininfo.minlen = 6;
+ pininfo.maxlen = 15;
+
+ prompt = "||Please enter the PIN";
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
+ {
+ err = pincb (pincb_arg, prompt, NULL);
+ if (err)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_verify_kp (app->slot, 0x81, &pininfo);
+ pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
+ }
+ else
+ {
+ err = pincb (pincb_arg, prompt, &pinvalue);
+ if (err)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_verify (app->slot, 0x81, pinvalue, strlen(pinvalue));
+ xfree (pinvalue);
+ }
+ if (err)
+ {
+ log_error ("PIN verification failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+ log_debug ("PIN verification succeeded\n");
+ return err;
+}
+
+
+
+/* Handler for the PKSIGN command.
+
+ Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required, the PINCB will be used to ask for the PIN;
+ that callback should return the PIN in an allocated buffer and
+ store that as the 3rd argument.
+
+ The API is somewhat inconsistent: The caller can either supply
+ a plain hash and the algorithm in hashalgo or a complete
+ DigestInfo structure. The former is detect by characteristic length
+ of the provided data (20,28,32,48 or 64 byte).
+
+ The function returns the RSA block in the size of the modulus or
+ the ECDSA signature in X9.62 format (SEQ/INT(r)/INT(s))
+*/
+static gpg_error_t
+do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
+ { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
+ 0x1C };
+ static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
+ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+ 0x00, 0x04, 0x20 };
+ static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
+ { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+ 0x00, 0x04, 0x30 };
+ static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
+ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+ 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+ 0x00, 0x04, 0x40 };
+
+ gpg_error_t err;
+ unsigned char cdsblk[256]; /* Raw PKCS#1 V1.5 block with padding
+ (RSA) or hash. */
+ prkdf_object_t prkdf; /* The private key object. */
+ size_t cdsblklen;
+ unsigned char algoid;
+ int sw;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (indatalen > 124) /* Limit for 1024 bit key */
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover
+ ||prkdf->usageflags.non_repudiation))
+ {
+ log_error ("key %s may not be used for signing\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ if (prkdf->keytype == KEY_TYPE_RSA)
+ {
+ algoid = 0x20;
+
+ cdsblklen = prkdf->keysize >> 3;
+ if (!cdsblklen)
+ cdsblklen = 256;
+
+ if (hashalgo == GCRY_MD_SHA1 && indatalen == 20)
+ apply_PKCS_padding (indata, indatalen,
+ sha1_prefix, sizeof(sha1_prefix),
+ cdsblk, cdsblklen);
+ else if (hashalgo == GCRY_MD_MD5 && indatalen == 20)
+ apply_PKCS_padding (indata, indatalen,
+ rmd160_prefix, sizeof(rmd160_prefix),
+ cdsblk, cdsblklen);
+ else if (hashalgo == GCRY_MD_SHA224 && indatalen == 28)
+ apply_PKCS_padding (indata, indatalen,
+ sha224_prefix, sizeof(sha224_prefix),
+ cdsblk, cdsblklen);
+ else if (hashalgo == GCRY_MD_SHA256 && indatalen == 32)
+ apply_PKCS_padding (indata, indatalen,
+ sha256_prefix, sizeof(sha256_prefix),
+ cdsblk, cdsblklen);
+ else if (hashalgo == GCRY_MD_SHA384 && indatalen == 48)
+ apply_PKCS_padding (indata, indatalen,
+ sha384_prefix, sizeof(sha384_prefix),
+ cdsblk, cdsblklen);
+ else if (hashalgo == GCRY_MD_SHA512 && indatalen == 64)
+ apply_PKCS_padding (indata, indatalen,
+ sha512_prefix, sizeof(sha512_prefix),
+ cdsblk, cdsblklen);
+ else /* Assume it's already a digest info or TLS_MD5SHA1 */
+ apply_PKCS_padding (indata, indatalen, NULL, 0, cdsblk, cdsblklen);
+ }
+ else
+ {
+ algoid = 0x70;
+ if (indatalen != 20 && indatalen != 28 && indatalen != 32
+ && indatalen != 48 && indatalen != 64)
+ {
+ cdsblklen = sizeof(cdsblk);
+ err = hash_from_digestinfo (indata, indatalen, cdsblk, &cdsblklen);
+ if (err)
+ {
+ log_error ("DigestInfo invalid: %s\n", gpg_strerror (err));
+ return err;
+ }
+ }
+ else
+ {
+ memcpy (cdsblk, indata, indatalen);
+ cdsblklen = indatalen;
+ }
+ }
+
+ err = verify_pin (app, pincb, pincb_arg);
+ if (err)
+ return err;
+
+ sw = apdu_send_le (app->slot, 1, 0x80, 0x68, prkdf->key_reference, algoid,
+ cdsblklen, cdsblk, 0, outdata, outdatalen);
+ return iso7816_map_sw (sw);
+}
+
+
+
+/* Handler for the PKAUTH command.
+
+ This is basically the same as the PKSIGN command but we first check
+ that the requested key is suitable for authentication; that is, it
+ must match the criteria used for the attribute $AUTHKEYID. See
+ do_sign for calling conventions; there is no HASHALGO, though. */
+static gpg_error_t
+do_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ gpg_error_t err;
+ prkdf_object_t prkdf;
+ int algo;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!prkdf->usageflags.sign)
+ {
+ log_error ("key %s may not be used for authentication\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ algo = indatalen == 36? MD_USER_TLS_MD5SHA1 : GCRY_MD_SHA1;
+ return do_sign (app, ctrl, keyidstr, algo, pincb, pincb_arg,
+ indata, indatalen, outdata, outdatalen);
+}
+
+
+
+/* Check PKCS#1 V1.5 padding and extract plain text. The function
+ * allocates a buffer for the plain text. The caller must release the
+ * buffer. */
+static gpg_error_t
+strip_PKCS15_padding(unsigned char *src, int srclen, unsigned char **dst,
+ size_t *dstlen)
+{
+ unsigned char *p;
+
+ if (srclen < 2)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ if (*src++ != 0x00)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ if (*src++ != 0x02)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ srclen -= 2;
+ while ((srclen > 0) && *src)
+ {
+ src++;
+ srclen--;
+ }
+
+ if (srclen < 2)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+
+ src++;
+ srclen--;
+
+ p = xtrymalloc (srclen);
+ if (!p)
+ return gpg_error_from_syserror ();
+
+ memcpy (p, src, srclen);
+ *dst = p;
+ *dstlen = srclen;
+
+ return 0;
+}
+
+
+/* Decrypt a PKCS#1 V1.5 formatted cryptogram using the referenced
+ key. */
+static gpg_error_t
+do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info)
+{
+ gpg_error_t err;
+ unsigned char p1blk[256]; /* Enciphered P1 block */
+ prkdf_object_t prkdf; /* The private key object. */
+ unsigned char *rspdata;
+ size_t rspdatalen;
+ size_t p1blklen;
+ int sw;
+
+ (void)ctrl;
+
+ if (!keyidstr || !*keyidstr || !indatalen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf);
+ if (err)
+ return err;
+ if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap))
+ {
+ log_error ("key %s may not be used for deciphering\n", keyidstr);
+ return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ }
+
+ if (prkdf->keytype != KEY_TYPE_RSA)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ p1blklen = prkdf->keysize >> 3;
+ if (!p1blklen)
+ p1blklen = 256;
+
+ /* The input may be shorter (due to MPIs not storing leading zeroes)
+ or longer than the block size. We put INDATA right aligned into
+ the buffer. If INDATA is longer than the block size we truncate
+ it on the left. */
+ memset (p1blk, 0, sizeof(p1blk));
+ if (indatalen > p1blklen)
+ memcpy (p1blk, (unsigned char *)indata + (indatalen - p1blklen), p1blklen);
+ else
+ memcpy (p1blk + (p1blklen - indatalen), indata, indatalen);
+
+
+ err = verify_pin(app, pincb, pincb_arg);
+ if (err)
+ return err;
+
+ sw = apdu_send_le (app->slot, 1, 0x80, 0x62, prkdf->key_reference, 0x21,
+ p1blklen, p1blk, 0, &rspdata, &rspdatalen);
+ err = iso7816_map_sw (sw);
+ if (err)
+ {
+ log_error ("Decrypt failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ err = strip_PKCS15_padding (rspdata, rspdatalen, outdata, outdatalen);
+ xfree (rspdata);
+
+ if (!err)
+ *r_info |= APP_DECIPHER_INFO_NOPAD;
+
+ return err;
+}
+
+
+
+/*
+ * Select the SmartCard-HSM application on the card in SLOT.
+ */
+gpg_error_t
+app_select_sc_hsm (app_t app)
+{
+ int slot = app->slot;
+ int rc;
+
+ rc = iso7816_select_application (slot, sc_hsm_aid, sizeof sc_hsm_aid, 0);
+ if (!rc)
+ {
+ app->apptype = APPTYPE_SC_HSM;
+
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ rc = read_meta (app);
+ if (rc)
+ goto leave;
+
+ app->fnc.deinit = do_deinit;
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readcert = do_readcert;
+ app->fnc.getattr = do_getattr;
+ app->fnc.setattr = NULL;
+ app->fnc.genkey = NULL;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = do_auth;
+ app->fnc.decipher = do_decipher;
+ app->fnc.change_pin = NULL;
+ app->fnc.check_pin = NULL;
+
+ leave:
+ if (rc)
+ do_deinit (app);
+ }
+
+ return rc;
+}
diff --git a/scd/app.c b/scd/app.c
new file mode 100644
index 0000000..846fc77
--- /dev/null
+++ b/scd/app.c
@@ -0,0 +1,1356 @@
+/* app.c - Application selection.
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <npth.h>
+
+#include "scdaemon.h"
+#include "../common/exechelp.h"
+#include "iso7816.h"
+#include "apdu.h"
+#include "../common/tlv.h"
+
+static npth_mutex_t app_list_lock;
+static app_t app_top;
+
+
+/* List of all supported apps. */
+static struct
+{
+ apptype_t apptype;
+ char const *name;
+} supported_app_list[] =
+ {{ APPTYPE_OPENPGP , "openpgp" },
+ { APPTYPE_NKS , "nks" },
+ { APPTYPE_P15 , "p15" },
+ { APPTYPE_GELDKARTE, "geldkarte" },
+ { APPTYPE_DINSIG , "dinsig" },
+ { APPTYPE_SC_HSM , "sc-hsm" },
+ { APPTYPE_NONE , NULL }
+ /* APPTYPE_UNDEFINED is special and not listed here. */
+ };
+
+
+
+static void
+print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
+{
+ ctrl_t ctrl = opaque;
+ char line[100];
+
+ if (ctrl)
+ {
+ snprintf (line, sizeof line, "%s %c %d %d", what, pc, cur, tot);
+ send_status_direct (ctrl, "PROGRESS", line);
+ }
+}
+
+
+/* Map an application type to a string. Never returns NULL. */
+const char *
+strapptype (apptype_t t)
+{
+ int i;
+
+ for (i=0; supported_app_list[i].apptype; i++)
+ if (supported_app_list[i].apptype == t)
+ return supported_app_list[i].name;
+ return t == APPTYPE_UNDEFINED? "undefined" : t? "?" : "none";
+}
+
+
+/* Return the apptype for NAME. */
+static apptype_t
+apptype_from_name (const char *name)
+{
+ int i;
+
+ if (!name)
+ return APPTYPE_NONE;
+
+ for (i=0; supported_app_list[i].apptype; i++)
+ if (!ascii_strcasecmp (supported_app_list[i].name, name))
+ return supported_app_list[i].apptype;
+ if (!ascii_strcasecmp ("undefined", name))
+ return APPTYPE_UNDEFINED;
+ return APPTYPE_NONE;
+}
+
+
+/* Lock the reader SLOT. This function shall be used right before
+ calling any of the actual application functions to serialize access
+ to the reader. We do this always even if the reader is not
+ actually used. This allows an actual connection to assume that it
+ never shares a reader (while performing one command). Returns 0 on
+ success; only then the unlock_reader function must be called after
+ returning from the handler. */
+static gpg_error_t
+lock_app (app_t app, ctrl_t ctrl)
+{
+ if (npth_mutex_lock (&app->lock))
+ {
+ gpg_error_t err = gpg_error_from_syserror ();
+ log_error ("failed to acquire APP lock for %p: %s\n",
+ app, gpg_strerror (err));
+ return err;
+ }
+
+ apdu_set_progress_cb (app->slot, print_progress_line, ctrl);
+ apdu_set_prompt_cb (app->slot, popup_prompt, ctrl);
+
+ return 0;
+}
+
+/* Release a lock on the reader. See lock_reader(). */
+static void
+unlock_app (app_t app)
+{
+ apdu_set_progress_cb (app->slot, NULL, NULL);
+ apdu_set_prompt_cb (app->slot, NULL, NULL);
+
+ if (npth_mutex_unlock (&app->lock))
+ {
+ gpg_error_t err = gpg_error_from_syserror ();
+ log_error ("failed to release APP lock for %p: %s\n",
+ app, gpg_strerror (err));
+ }
+}
+
+
+/* This function may be called to print information pertaining to the
+ current state of this module to the log. */
+void
+app_dump_state (void)
+{
+ app_t a;
+
+ npth_mutex_lock (&app_list_lock);
+ for (a = app_top; a; a = a->next)
+ log_info ("app_dump_state: app=%p type='%s'\n", a, strapptype (a->apptype));
+ npth_mutex_unlock (&app_list_lock);
+}
+
+/* Check whether the application NAME is allowed. This does not mean
+ we have support for it though. */
+static int
+is_app_allowed (const char *name)
+{
+ strlist_t l;
+
+ for (l=opt.disabled_applications; l; l = l->next)
+ if (!strcmp (l->d, name))
+ return 0; /* no */
+ return 1; /* yes */
+}
+
+
+static gpg_error_t
+check_conflict (app_t app, const char *name)
+{
+ if (!app || !name
+ || (app->apptype && app->apptype == apptype_from_name (name)))
+ return 0;
+
+ if (app->apptype && app->apptype == APPTYPE_UNDEFINED)
+ return 0;
+
+ log_info ("application '%s' in use - can't switch\n",
+ strapptype (app->apptype));
+
+ return gpg_error (GPG_ERR_CONFLICT);
+}
+
+/* This function is used by the serialno command to check for an
+ application conflict which may appear if the serialno command is
+ used to request a specific application and the connection has
+ already done a select_application. */
+gpg_error_t
+check_application_conflict (const char *name, app_t app)
+{
+ return check_conflict (app, name);
+}
+
+
+gpg_error_t
+app_reset (app_t app, ctrl_t ctrl, int send_reset)
+{
+ gpg_error_t err = 0;
+
+ if (send_reset)
+ {
+ int sw;
+
+ lock_app (app, ctrl);
+ sw = apdu_reset (app->slot);
+ if (sw)
+ err = gpg_error (GPG_ERR_CARD_RESET);
+
+ app->reset_requested = 1;
+ unlock_app (app);
+
+ scd_kick_the_loop ();
+ gnupg_sleep (1);
+ }
+ else
+ {
+ ctrl->app_ctx = NULL;
+ release_application (app, 0);
+ }
+
+ return err;
+}
+
+static gpg_error_t
+app_new_register (int slot, ctrl_t ctrl, const char *name,
+ int periodical_check_needed)
+{
+ gpg_error_t err = 0;
+ app_t app = NULL;
+ unsigned char *result = NULL;
+ size_t resultlen;
+ int want_undefined;
+
+ /* Need to allocate a new one. */
+ app = xtrycalloc (1, sizeof *app);
+ if (!app)
+ {
+ err = gpg_error_from_syserror ();
+ log_info ("error allocating context: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ app->slot = slot;
+ app->card_status = (unsigned int)-1;
+
+ if (npth_mutex_init (&app->lock, NULL))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error initializing mutex: %s\n", gpg_strerror (err));
+ xfree (app);
+ return err;
+ }
+
+ err = lock_app (app, ctrl);
+ if (err)
+ {
+ xfree (app);
+ return err;
+ }
+
+ want_undefined = (name && !strcmp (name, "undefined"));
+
+ /* Try to read the GDO file first to get a default serial number.
+ We skip this if the undefined application has been requested. */
+ if (!want_undefined)
+ {
+ err = iso7816_select_file (slot, 0x3F00, 1);
+ if (gpg_err_code (err) == GPG_ERR_CARD)
+ {
+ /* Might be SW==0x7D00. Let's test whether it is a Yubikey
+ * by selecting its manager application and then reading the
+ * config. */
+ static char const yk_aid[] =
+ { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17 }; /*MGR*/
+ static char const otp_aid[] =
+ { 0xA0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01 }; /*OTP*/
+ unsigned char *buf;
+ size_t buflen;
+ const unsigned char *s0;
+ unsigned char formfactor;
+ size_t n;
+
+ if (!iso7816_select_application (slot, yk_aid, sizeof yk_aid,
+ 0x0001)
+ && !iso7816_apdu_direct (slot, "\x00\x1d\x00\x00\x00", 5, 0,
+ NULL, &buf, &buflen))
+ {
+ app->cardtype = CARDTYPE_YUBIKEY;
+ if (opt.verbose)
+ {
+ log_info ("Yubico: config=");
+ log_printhex (buf, buflen, "");
+ }
+
+ /* We skip the first byte which seems to be the total
+ * length of the config data. */
+ if (buflen > 1)
+ {
+ s0 = find_tlv (buf+1, buflen-1, 0x04, &n); /* Form factor */
+ formfactor = (s0 && n == 1)? *s0 : 0;
+
+ s0 = find_tlv (buf+1, buflen-1, 0x02, &n); /* Serial */
+ if (s0 && n <= 4)
+ {
+ app->serialno = xtrymalloc (3 + 1 + 4);
+ if (app->serialno)
+ {
+ app->serialnolen = 3 + 1 + 4;
+ app->serialno[0] = 0xff;
+ app->serialno[1] = 0x02;
+ app->serialno[2] = 0x0;
+ app->serialno[3] = formfactor;
+ memset (app->serialno + 4, 0, 4 - n);
+ memcpy (app->serialno + 4 + 4 - n, s0, n);
+ err = app_munge_serialno (app);
+ }
+ }
+
+ s0 = find_tlv (buf+1, buflen-1, 0x05, &n); /* version */
+ if (s0 && n == 3)
+ app->cardversion = ((s0[0]<<16)|(s0[1]<<8)|s0[2]);
+ else if (!s0)
+ {
+ /* No version - this is not a Yubikey 5. We now
+ * switch to the OTP app and take the first
+ * three bytes of the response as version
+ * number. */
+ xfree (buf);
+ buf = NULL;
+ if (!iso7816_select_application_ext (slot,
+ otp_aid, sizeof otp_aid,
+ 1, &buf, &buflen)
+ && buflen > 3)
+ app->cardversion = ((buf[0]<<16)|(buf[1]<<8)|buf[2]);
+ }
+ }
+ xfree (buf);
+ }
+ }
+ else
+ {
+ unsigned char *atr;
+ size_t atrlen;
+
+ /* This is heuristics to identify different implementations. */
+ atr = apdu_get_atr (slot, &atrlen);
+ if (atr)
+ {
+ if (atrlen == 21 && atr[2] == 0x11)
+ app->cardtype = CARDTYPE_GNUK;
+ else if (atrlen == 21 && atr[7] == 0x75)
+ app->cardtype = CARDTYPE_ZEITCONTROL;
+ xfree (atr);
+ }
+ }
+
+ if (!err && app->cardtype != CARDTYPE_YUBIKEY)
+ err = iso7816_select_file (slot, 0x2F02, 0);
+ if (!err && app->cardtype != CARDTYPE_YUBIKEY)
+ err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!err && app->cardtype != CARDTYPE_YUBIKEY)
+ {
+ size_t n;
+ const unsigned char *p;
+
+ p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
+ if (p)
+ resultlen -= (p-result);
+ if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+ {
+ /* The object it does not fit into the buffer. This is an
+ invalid encoding (or the buffer is too short. However, I
+ have some test cards with such an invalid encoding and
+ therefore I use this ugly workaround to return something
+ I can further experiment with. */
+ log_info ("enabling BMI testcard workaround\n");
+ n--;
+ }
+
+ if (p && n <= resultlen)
+ {
+ /* The GDO file is pretty short, thus we simply reuse it for
+ storing the serial number. */
+ memmove (result, p, n);
+ app->serialno = result;
+ app->serialnolen = n;
+ err = app_munge_serialno (app);
+ if (err)
+ goto leave;
+ }
+ else
+ xfree (result);
+ result = NULL;
+ }
+ }
+
+ /* For certain error codes, there is no need to try more. */
+ if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
+ || gpg_err_code (err) == GPG_ERR_ENODEV)
+ goto leave;
+
+ /* Figure out the application to use. */
+ if (want_undefined)
+ {
+ /* We switch to the "undefined" application only if explicitly
+ requested. */
+ app->apptype = APPTYPE_UNDEFINED;
+ err = 0;
+ }
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+
+ /* Fixme: Use a table like we do in 2.3. */
+ if (err && is_app_allowed ("openpgp")
+ && (!name || !strcmp (name, "openpgp")))
+ err = app_select_openpgp (app);
+ if (err && is_app_allowed ("nks") && (!name || !strcmp (name, "nks")))
+ err = app_select_nks (app);
+ if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
+ err = app_select_p15 (app);
+ if (err && is_app_allowed ("geldkarte")
+ && (!name || !strcmp (name, "geldkarte")))
+ err = app_select_geldkarte (app);
+ if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
+ err = app_select_dinsig (app);
+ if (err && is_app_allowed ("sc-hsm") && (!name || !strcmp (name, "sc-hsm")))
+ err = app_select_sc_hsm (app);
+ if (err && name && gpg_err_code (err) != GPG_ERR_OBJ_TERM_STATE)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ leave:
+ if (err)
+ {
+ if (name)
+ log_info ("can't select application '%s': %s\n",
+ name, gpg_strerror (err));
+ else
+ log_info ("no supported card application found: %s\n",
+ gpg_strerror (err));
+ unlock_app (app);
+ xfree (app);
+ return err;
+ }
+
+ app->periodical_check_needed = periodical_check_needed;
+
+ npth_mutex_lock (&app_list_lock);
+ app->next = app_top;
+ app_top = app;
+ npth_mutex_unlock (&app_list_lock);
+ unlock_app (app);
+ return 0;
+}
+
+/* If called with NAME as NULL, select the best fitting application
+ and return a context; otherwise select the application with NAME
+ and return a context. Returns an error code and stores NULL at
+ R_APP if no application was found or no card is present. */
+gpg_error_t
+select_application (ctrl_t ctrl, const char *name, app_t *r_app,
+ int scan, const unsigned char *serialno_bin,
+ size_t serialno_bin_len)
+{
+ gpg_error_t err = 0;
+ app_t a, a_prev = NULL;
+
+ *r_app = NULL;
+
+ if (scan || !app_top)
+ {
+ struct dev_list *l;
+ int new_app = 0;
+
+ /* Scan the devices to find new device(s). */
+ err = apdu_dev_list_start (opt.reader_port, &l);
+ if (err)
+ return err;
+
+ while (1)
+ {
+ int slot;
+ int periodical_check_needed_this;
+
+ slot = apdu_open_reader (l, !app_top);
+ if (slot < 0)
+ break;
+
+ periodical_check_needed_this = apdu_connect (slot);
+ if (periodical_check_needed_this < 0)
+ {
+ /* We close a reader with no card. */
+ err = gpg_error (GPG_ERR_ENODEV);
+ }
+ else
+ {
+ err = app_new_register (slot, ctrl, name,
+ periodical_check_needed_this);
+ new_app++;
+ }
+
+ if (err)
+ apdu_close_reader (slot);
+ }
+
+ apdu_dev_list_finish (l);
+
+ /* If new device(s), kick the scdaemon loop. */
+ if (new_app)
+ scd_kick_the_loop ();
+ }
+
+ npth_mutex_lock (&app_list_lock);
+ for (a = app_top; a; a = a->next)
+ {
+ lock_app (a, ctrl);
+ if (serialno_bin == NULL)
+ break;
+ if (a->serialnolen == serialno_bin_len
+ && !memcmp (a->serialno, serialno_bin, a->serialnolen))
+ break;
+ unlock_app (a);
+ a_prev = a;
+ }
+
+ if (a)
+ {
+ err = check_conflict (a, name);
+ if (!err)
+ {
+ a->ref_count++;
+ *r_app = a;
+ if (a_prev)
+ {
+ a_prev->next = a->next;
+ a->next = app_top;
+ app_top = a;
+ }
+ }
+ unlock_app (a);
+ }
+ else
+ err = gpg_error (GPG_ERR_ENODEV);
+
+ npth_mutex_unlock (&app_list_lock);
+
+ return err;
+}
+
+
+char *
+get_supported_applications (void)
+{
+ const char *list[] = {
+ "openpgp",
+ "nks",
+ "p15",
+ "geldkarte",
+ "dinsig",
+ "sc-hsm",
+ /* Note: "undefined" is not listed here because it needs special
+ treatment by the client. */
+ NULL
+ };
+ int idx;
+ size_t nbytes;
+ char *buffer, *p;
+
+ for (nbytes=1, idx=0; list[idx]; idx++)
+ nbytes += strlen (list[idx]) + 1 + 1;
+
+ buffer = xtrymalloc (nbytes);
+ if (!buffer)
+ return NULL;
+
+ for (p=buffer, idx=0; list[idx]; idx++)
+ if (is_app_allowed (list[idx]))
+ p = stpcpy (stpcpy (p, list[idx]), ":\n");
+ *p = 0;
+
+ return buffer;
+}
+
+
+/* Deallocate the application. */
+static void
+deallocate_app (app_t app)
+{
+ app_t a, a_prev = NULL;
+
+ for (a = app_top; a; a = a->next)
+ if (a == app)
+ {
+ if (a_prev == NULL)
+ app_top = a->next;
+ else
+ a_prev->next = a->next;
+ break;
+ }
+ else
+ a_prev = a;
+
+ if (app->ref_count)
+ log_error ("trying to release context used yet (%d)\n", app->ref_count);
+
+ if (app->fnc.deinit)
+ {
+ app->fnc.deinit (app);
+ app->fnc.deinit = NULL;
+ }
+
+ xfree (app->serialno);
+
+ unlock_app (app);
+ xfree (app);
+}
+
+/* Free the resources associated with the application APP. APP is
+ allowed to be NULL in which case this is a no-op. Note that we are
+ using reference counting to track the users of the application and
+ actually deferring the deallocation to allow for a later reuse by
+ a new connection. */
+void
+release_application (app_t app, int locked_already)
+{
+ if (!app)
+ return;
+
+ /* We don't deallocate app here. Instead, we keep it. This is
+ useful so that a card does not get reset even if only one session
+ is using the card - this way the PIN cache and other cached data
+ are preserved. */
+
+ if (!locked_already)
+ lock_app (app, NULL);
+
+ if (!app->ref_count)
+ log_bug ("trying to release an already released context\n");
+
+ --app->ref_count;
+ if (!locked_already)
+ unlock_app (app);
+}
+
+
+
+/* The serial number may need some cosmetics. Do it here. This
+ function shall only be called once after a new serial number has
+ been put into APP->serialno.
+
+ Prefixes we use:
+
+ FF 00 00 = For serial numbers starting with an FF
+ FF 01 00 = Some german p15 cards return an empty serial number so the
+ serial number from the EF(TokenInfo) is used instead.
+ FF 7F 00 = No serialno.
+
+ All other serial number not starting with FF are used as they are.
+*/
+gpg_error_t
+app_munge_serialno (app_t app)
+{
+ if (app->serialnolen && app->serialno[0] == 0xff)
+ {
+ /* The serial number starts with our special prefix. This
+ requires that we put our default prefix "FF0000" in front. */
+ unsigned char *p = xtrymalloc (app->serialnolen + 3);
+ if (!p)
+ return gpg_error_from_syserror ();
+ memcpy (p, "\xff\0", 3);
+ memcpy (p+3, app->serialno, app->serialnolen);
+ app->serialnolen += 3;
+ xfree (app->serialno);
+ app->serialno = p;
+ }
+ else if (!app->serialnolen)
+ {
+ unsigned char *p = xtrymalloc (3);
+ if (!p)
+ return gpg_error_from_syserror ();
+ memcpy (p, "\xff\x7f", 3);
+ app->serialnolen = 3;
+ xfree (app->serialno);
+ app->serialno = p;
+ }
+ return 0;
+}
+
+
+
+/* Retrieve the serial number of the card. The serial number is
+ returned as a malloced string (hex encoded) in SERIAL. Caller must
+ free SERIAL unless the function returns an error. */
+char *
+app_get_serialno (app_t app)
+{
+ char *serial;
+
+ if (!app)
+ return NULL;
+
+ if (!app->serialnolen)
+ serial = xtrystrdup ("FF7F00");
+ else
+ serial = bin2hex (app->serialno, app->serialnolen, NULL);
+
+ return serial;
+}
+
+
+/* Return an allocated string with the serial number in a format to be
+ * show to the user. With NOFALLBACK set to true return NULL if such an
+ * abbreviated S/N is not available, else return the full serial
+ * number as a hex string. May return NULL on malloc problem. */
+char *
+app_get_dispserialno (app_t app, int nofallback)
+{
+ char *result, *p;
+ unsigned long sn;
+
+ if (app && app->serialno && app->serialnolen == 3+1+4
+ && !memcmp (app->serialno, "\xff\x02\x00", 3))
+ {
+ /* This is a 4 byte S/N of a Yubikey which seems to be printed
+ * on the token in decimal. Maybe they will print larger S/N
+ * also in decimal but we can't be sure, thus do it only for
+ * these 32 bit numbers. */
+ sn = app->serialno[4] * 16777216;
+ sn += app->serialno[5] * 65536;
+ sn += app->serialno[6] * 256;
+ sn += app->serialno[7];
+ if ((app->cardversion >> 16) >= 5)
+ result = xtryasprintf ("%lu %03lu %03lu",
+ (sn/1000000ul),
+ (sn/1000ul % 1000ul),
+ (sn % 1000ul));
+ else
+ result = xtryasprintf ("%lu", sn);
+ }
+ else if (app && app->cardtype == CARDTYPE_YUBIKEY)
+ {
+ /* Get back the printed Yubikey number from the OpenPGP AID
+ * Example: D2760001240100000006120808620000
+ */
+ result = app_get_serialno (app);
+ if (result && strlen (result) >= 28 && !strncmp (result+16, "0006", 4))
+ {
+ sn = atoi_4 (result+20) * 10000;
+ sn += atoi_4 (result+24);
+ if ((app->cardversion >> 16) >= 5)
+ p = xtryasprintf ("%lu %03lu %03lu",
+ (sn/1000000ul),
+ (sn/1000ul % 1000ul),
+ (sn % 1000ul));
+ else
+ p = xtryasprintf ("%lu", sn);
+ if (p)
+ {
+ xfree (result);
+ result = p;
+ }
+ }
+ else if (nofallback)
+ {
+ xfree (result);
+ result = NULL;
+ }
+ }
+ else if (app && app->apptype == APPTYPE_OPENPGP)
+ {
+ /* Extract number from standard OpenPGP AID. */
+ result = app_get_serialno (app);
+ if (result && strlen (result) > 16+12)
+ {
+ memcpy (result, result+16, 4);
+ result[4] = ' ';
+ memcpy (result+5, result+20, 8);
+ result[13] = 0;
+ }
+ else if (nofallback)
+ {
+ xfree (result);
+ result = NULL;
+ }
+ }
+ else if (nofallback)
+ result = NULL; /* No Abbreviated S/N. */
+ else
+ result = app_get_serialno (app);
+
+ return result;
+}
+
+
+/* Write out the application specifig status lines for the LEARN
+ command. */
+gpg_error_t
+app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
+{
+ gpg_error_t err;
+
+ if (!app)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->fnc.learn_status)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* We do not send APPTYPE if only keypairinfo is requested. */
+ if (app->apptype && !(flags & 1))
+ send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype));
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.learn_status (app, ctrl, flags);
+ unlock_app (app);
+ return err;
+}
+
+
+/* Read the certificate with id CERTID (as returned by learn_status in
+ the CERTINFO status lines) and return it in the freshly allocated
+ buffer put into CERT and the length of the certificate put into
+ CERTLEN. */
+gpg_error_t
+app_readcert (app_t app, ctrl_t ctrl, const char *certid,
+ unsigned char **cert, size_t *certlen)
+{
+ gpg_error_t err;
+
+ if (!app)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.readcert)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.readcert (app, certid, cert, certlen);
+ unlock_app (app);
+ return err;
+}
+
+
+/* Read the key with ID KEYID. On success a canonical encoded
+ S-expression with the public key will get stored at PK and its
+ length (for assertions) at PKLEN; the caller must release that
+ buffer. On error NULL will be stored at PK and PKLEN and an error
+ code returned.
+
+ This function might not be supported by all applications. */
+gpg_error_t
+app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid,
+ unsigned char **pk, size_t *pklen)
+{
+ gpg_error_t err;
+
+ if (pk)
+ *pk = NULL;
+ if (pklen)
+ *pklen = 0;
+
+ if (!app || !keyid || !pk || !pklen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.readkey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err= app->fnc.readkey (app, ctrl, keyid,
+ advanced? APP_READKEY_FLAG_ADVANCED : 0,
+ pk, pklen);
+ unlock_app (app);
+ return err;
+}
+
+
+/* Perform a GETATTR operation. */
+gpg_error_t
+app_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ gpg_error_t err;
+
+ if (!app || !name || !*name)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+
+ if (app->apptype && name && !strcmp (name, "APPTYPE"))
+ {
+ send_status_direct (ctrl, "APPTYPE", strapptype (app->apptype));
+ return 0;
+ }
+ if (name && !strcmp (name, "SERIALNO"))
+ {
+ char *serial;
+
+ serial = app_get_serialno (app);
+ if (!serial)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ send_status_direct (ctrl, "SERIALNO", serial);
+ xfree (serial);
+ return 0;
+ }
+
+ if (!app->fnc.getattr)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.getattr (app, ctrl, name);
+ unlock_app (app);
+ return err;
+}
+
+/* Perform a SETATTR operation. */
+gpg_error_t
+app_setattr (app_t app, ctrl_t ctrl, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ gpg_error_t err;
+
+ if (!app || !name || !*name || !value)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.setattr)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.setattr (app, ctrl, name, pincb, pincb_arg, value, valuelen);
+ unlock_app (app);
+ return err;
+}
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+gpg_error_t
+app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ gpg_error_t err;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.sign)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.sign (app, ctrl, keyidstr, hashalgo,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation sign result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+/* Create the signature using the INTERNAL AUTHENTICATE command and
+ return the allocated result in OUTDATA. If a PIN is required the
+ PINCB will be used to ask for the PIN; it should return the PIN in
+ an allocated buffer and put it into PIN. */
+gpg_error_t
+app_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ gpg_error_t err;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.auth)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.auth (app, ctrl, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation auth result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+gpg_error_t
+app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen,
+ unsigned int *r_info)
+{
+ gpg_error_t err;
+
+ *r_info = 0;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.decipher)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.decipher (app, ctrl, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen,
+ r_info);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation decipher result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Perform the WRITECERT operation. */
+gpg_error_t
+app_writecert (app_t app, ctrl_t ctrl,
+ const char *certidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *data, size_t datalen)
+{
+ gpg_error_t err;
+
+ if (!app || !certidstr || !*certidstr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.writecert)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.writecert (app, ctrl, certidstr,
+ pincb, pincb_arg, data, datalen);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation writecert result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Perform the WRITEKEY operation. */
+gpg_error_t
+app_writekey (app_t app, ctrl_t ctrl,
+ const char *keyidstr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+
+ if (!app || !keyidstr || !*keyidstr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.writekey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.writekey (app, ctrl, keyidstr, flags,
+ pincb, pincb_arg, keydata, keydatalen);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation writekey result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Perform a SETATTR operation. */
+gpg_error_t
+app_genkey (app_t app, ctrl_t ctrl, const char *keynostr,
+ const char *keytype, unsigned int flags, time_t createtime,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+
+ if (!app || !keynostr || !*keynostr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.genkey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.genkey (app, ctrl, keynostr, keytype, flags,
+ createtime, pincb, pincb_arg);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation genkey result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Perform a GET CHALLENGE operation. This function is special as it
+ directly accesses the card without any application specific
+ wrapper. */
+gpg_error_t
+app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer)
+{
+ gpg_error_t err;
+
+ if (!app || !nbytes || !buffer)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = iso7816_get_challenge (app->slot, nbytes, buffer);
+ unlock_app (app);
+ return err;
+}
+
+
+
+/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
+gpg_error_t
+app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+ unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+
+ if (!app || !chvnostr || !*chvnostr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.change_pin)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.change_pin (app, ctrl, chvnostr, flags, pincb, pincb_arg);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation change_pin result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Perform a VERIFY operation without doing anything else. This may
+ be used to initialize a the PIN cache for long lasting other
+ operations. Its use is highly application dependent. */
+gpg_error_t
+app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ gpg_error_t err;
+
+ if (!app || !keyidstr || !*keyidstr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->ref_count)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.check_pin)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_app (app, ctrl);
+ if (err)
+ return err;
+ err = app->fnc.check_pin (app, ctrl, keyidstr, pincb, pincb_arg);
+ unlock_app (app);
+ if (opt.verbose)
+ log_info ("operation check_pin result: %s\n", gpg_strerror (err));
+ return err;
+}
+
+static void
+report_change (int slot, int old_status, int cur_status)
+{
+ char *homestr, *envstr;
+ char *fname;
+ char templ[50];
+ estream_t fp;
+
+ snprintf (templ, sizeof templ, "reader_%d.status", slot);
+ fname = make_filename (gnupg_homedir (), templ, NULL );
+ fp = es_fopen (fname, "w");
+ if (fp)
+ {
+ es_fprintf (fp, "%s\n",
+ (cur_status & 1)? "USABLE":
+ (cur_status & 4)? "ACTIVE":
+ (cur_status & 2)? "PRESENT": "NOCARD");
+ es_fclose (fp);
+ }
+ xfree (fname);
+
+ homestr = make_filename (gnupg_homedir (), NULL);
+ if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
+ log_error ("out of core while building environment\n");
+ else
+ {
+ gpg_error_t err;
+ const char *args[9], *envs[2];
+ char numbuf1[30], numbuf2[30], numbuf3[30];
+
+ envs[0] = envstr;
+ envs[1] = NULL;
+
+ sprintf (numbuf1, "%d", slot);
+ sprintf (numbuf2, "0x%04X", old_status);
+ sprintf (numbuf3, "0x%04X", cur_status);
+ args[0] = "--reader-port";
+ args[1] = numbuf1;
+ args[2] = "--old-code";
+ args[3] = numbuf2;
+ args[4] = "--new-code";
+ args[5] = numbuf3;
+ args[6] = "--status";
+ args[7] = ((cur_status & 1)? "USABLE":
+ (cur_status & 4)? "ACTIVE":
+ (cur_status & 2)? "PRESENT": "NOCARD");
+ args[8] = NULL;
+
+ fname = make_filename (gnupg_homedir (), "scd-event", NULL);
+ err = gnupg_spawn_process_detached (fname, args, envs);
+ if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
+ log_error ("failed to run event handler '%s': %s\n",
+ fname, gpg_strerror (err));
+ xfree (fname);
+ xfree (envstr);
+ }
+ xfree (homestr);
+}
+
+int
+scd_update_reader_status_file (void)
+{
+ app_t a, app_next;
+ int periodical_check_needed = 0;
+
+ npth_mutex_lock (&app_list_lock);
+ for (a = app_top; a; a = app_next)
+ {
+ int sw;
+ unsigned int status;
+
+ lock_app (a, NULL);
+ app_next = a->next;
+
+ if (a->reset_requested)
+ status = 0;
+ else
+ {
+ sw = apdu_get_status (a->slot, 0, &status);
+ if (sw == SW_HOST_NO_READER)
+ {
+ /* Most likely the _reader_ has been unplugged. */
+ status = 0;
+ }
+ else if (sw)
+ {
+ /* Get status failed. Ignore that. */
+ if (a->periodical_check_needed)
+ periodical_check_needed = 1;
+ unlock_app (a);
+ continue;
+ }
+ }
+
+ if (a->card_status != status)
+ {
+ report_change (a->slot, a->card_status, status);
+ send_client_notifications (a, status == 0);
+
+ if (status == 0)
+ {
+ log_debug ("Removal of a card: %d\n", a->slot);
+ apdu_close_reader (a->slot);
+ deallocate_app (a);
+ }
+ else
+ {
+ a->card_status = status;
+ if (a->periodical_check_needed)
+ periodical_check_needed = 1;
+ unlock_app (a);
+ }
+ }
+ else
+ {
+ if (a->periodical_check_needed)
+ periodical_check_needed = 1;
+ unlock_app (a);
+ }
+ }
+ npth_mutex_unlock (&app_list_lock);
+
+ return periodical_check_needed;
+}
+
+/* This function must be called once to initialize this module. This
+ has to be done before a second thread is spawned. We can't do the
+ static initialization because Pth emulation code might not be able
+ to do a static init; in particular, it is not possible for W32. */
+gpg_error_t
+initialize_module_command (void)
+{
+ gpg_error_t err;
+
+ if (npth_mutex_init (&app_list_lock, NULL))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("app: error initializing mutex: %s\n", gpg_strerror (err));
+ return err;
+ }
+
+ return apdu_init ();
+}
+
+void
+app_send_card_list (ctrl_t ctrl)
+{
+ app_t a;
+ char buf[65];
+
+ npth_mutex_lock (&app_list_lock);
+ for (a = app_top; a; a = a->next)
+ {
+ if (DIM (buf) < 2 * a->serialnolen + 1)
+ continue;
+
+ bin2hex (a->serialno, a->serialnolen, buf);
+ send_status_direct (ctrl, "SERIALNO", buf);
+ }
+ npth_mutex_unlock (&app_list_lock);
+}
diff --git a/scd/atr.c b/scd/atr.c
new file mode 100644
index 0000000..4f5a3b8
--- /dev/null
+++ b/scd/atr.c
@@ -0,0 +1,290 @@
+/* atr.c - ISO 7816 ATR functions
+ * Copyright (C) 2003, 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <gpg-error.h>
+#include "../common/logging.h"
+#include "atr.h"
+
+static int const fi_table[16] = { 0, 372, 558, 744, 1116,1488, 1860, -1,
+ -1, 512, 768, 1024, 1536, 2048, -1, -1 };
+static int const di_table[16] = { -1, 1, 2, 4, 8, 16, -1, -1,
+ 0, -1, -2, -4, -8, -16, -32, -64};
+
+
+/* Dump the ATR in (BUFFER,BUFLEN) to a human readable format and
+ return that as a malloced buffer. The caller must release this
+ buffer using es_free! On error this function returns NULL and sets
+ ERRNO. */
+char *
+atr_dump (const void *buffer, size_t buflen)
+{
+ const unsigned char *atr = buffer;
+ size_t atrlen = buflen;
+ estream_t fp;
+ int have_ta, have_tb, have_tc, have_td;
+ int n_historical;
+ int idx, val;
+ unsigned char chksum;
+ char *result;
+
+ fp = es_fopenmem (0, "rwb,samethread");
+ if (!fp)
+ return NULL;
+
+ if (!atrlen)
+ {
+ es_fprintf (fp, "error: empty ATR\n");
+ goto bailout;
+ }
+
+ for (idx=0; idx < atrlen ; idx++)
+ es_fprintf (fp, "%s%02X", idx?" ":"", atr[idx]);
+ es_putc ('\n', fp);
+
+ if (*atr == 0x3b)
+ es_fputs ("Direct convention\n", fp);
+ else if (*atr == 0x3f)
+ es_fputs ("Inverse convention\n", fp);
+ else
+ es_fprintf (fp,"error: invalid TS character 0x%02x\n", *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+
+ chksum = *atr;
+ for (idx=1; idx < atrlen-1; idx++)
+ chksum ^= atr[idx];
+
+ have_ta = !!(*atr & 0x10);
+ have_tb = !!(*atr & 0x20);
+ have_tc = !!(*atr & 0x40);
+ have_td = !!(*atr & 0x80);
+ n_historical = (*atr & 0x0f);
+ es_fprintf (fp, "%d historical characters indicated\n", n_historical);
+
+ if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
+ es_fputs ("error: ATR shorter than indicated by format character\n", fp);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+
+ if (have_ta)
+ {
+ es_fputs ("TA1: F=", fp);
+ val = fi_table[(*atr >> 4) & 0x0f];
+ if (!val)
+ es_fputs ("internal clock", fp);
+ else if (val == -1)
+ es_fputs ("RFU", fp);
+ else
+ es_fprintf (fp, "%d", val);
+ es_fputs (" D=", fp);
+ val = di_table[*atr & 0x0f];
+ if (!val)
+ es_fputs ("[impossible value]\n", fp);
+ else if (val == -1)
+ es_fputs ("RFU\n", fp);
+ else if (val < 0 )
+ es_fprintf (fp, "1/%d\n", val);
+ else
+ es_fprintf (fp, "%d\n", val);
+
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tb)
+ {
+ es_fprintf (fp, "TB1: II=%d PI1=%d%s\n",
+ ((*atr >> 5) & 3), (*atr & 0x1f),
+ (*atr & 0x80)? " [high bit not cleared]":"");
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tc)
+ {
+ if (*atr == 255)
+ es_fputs ("TC1: guard time shortened to 1 etu\n", fp);
+ else
+ es_fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr);
+
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_td)
+ {
+ have_ta = !!(*atr & 0x10);
+ have_tb = !!(*atr & 0x20);
+ have_tc = !!(*atr & 0x40);
+ have_td = !!(*atr & 0x80);
+ es_fprintf (fp, "TD1: protocol T%d supported\n", (*atr & 0x0f));
+
+ if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
+ es_fputs ("error: ATR shorter than indicated by format character\n",
+ fp);
+
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+ else
+ have_ta = have_tb = have_tc = have_td = 0;
+
+ if (have_ta)
+ {
+ es_fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n",
+ (*atr & 0x80)? "no-":"",
+ (*atr & 0x10)? "im": "ex",
+ (*atr & 0x0f));
+ if ((*atr & 0x60))
+ es_fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tb)
+ {
+ es_fprintf (fp, "TB2: PI2=%d\n", *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tc)
+ {
+ es_fprintf (fp, "TC2: PWI=%d\n", *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_td)
+ {
+ have_ta = !!(*atr & 0x10);
+ have_tb = !!(*atr & 0x20);
+ have_tc = !!(*atr & 0x40);
+ have_td = !!(*atr & 0x80);
+ es_fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f);
+
+ if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
+ es_fputs ("error: ATR shorter than indicated by format character\n",
+ fp);
+
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+ else
+ have_ta = have_tb = have_tc = have_td = 0;
+
+ for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++)
+ {
+ if (have_ta)
+ {
+ es_fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tb)
+ {
+ es_fprintf (fp, "TB%d: BWI=%d CWI=%d\n",
+ idx, (*atr >> 4) & 0x0f, *atr & 0x0f);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_tc)
+ {
+ es_fprintf (fp, "TC%d: 0x%02X\n", idx, *atr);
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+
+ if (have_td)
+ {
+ have_ta = !!(*atr & 0x10);
+ have_tb = !!(*atr & 0x20);
+ have_tc = !!(*atr & 0x40);
+ have_td = !!(*atr & 0x80);
+ es_fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f);
+
+ if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
+ es_fputs ("error: "
+ "ATR shorter than indicated by format character\n",
+ fp);
+
+ if (!--atrlen)
+ goto bailout;
+ atr++;
+ }
+ else
+ have_ta = have_tb = have_tc = have_td = 0;
+ }
+
+ if (n_historical + 1 > atrlen)
+ es_fputs ("error: ATR shorter than required for historical bytes "
+ "and checksum\n", fp);
+
+ if (n_historical)
+ {
+ es_fputs ("HCH:", fp);
+ for (; n_historical && atrlen ; n_historical--, atrlen--, atr++)
+ es_fprintf (fp, " %02X", *atr);
+ es_putc ('\n', fp);
+ }
+
+ if (!atrlen)
+ es_fputs ("error: checksum missing\n", fp);
+ else if (*atr == chksum)
+ es_fprintf (fp, "TCK: %02X (good)\n", *atr);
+ else
+ es_fprintf (fp, "TCK: %02X (bad; computed %02X)\n", *atr, chksum);
+
+ atrlen--;
+ if (atrlen)
+ es_fprintf (fp, "error: %u bytes garbage at end of ATR\n",
+ (unsigned int)atrlen );
+
+ bailout:
+ es_putc ('\0', fp); /* We want a string. */
+ if (es_fclose_snatch (fp, (void**)&result, NULL))
+ {
+ log_error ("oops: es_fclose_snatch failed: %s\n", strerror (errno));
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/scd/atr.h b/scd/atr.h
new file mode 100644
index 0000000..d39e243
--- /dev/null
+++ b/scd/atr.h
@@ -0,0 +1,27 @@
+/* atr.h - ISO 7816 ATR functions
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ATR_H
+#define ATR_H
+
+char *atr_dump (const void *buffer, size_t buflen);
+
+
+
+#endif /*ATR_H*/
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
new file mode 100644
index 0000000..214165f
--- /dev/null
+++ b/scd/ccid-driver.c
@@ -0,0 +1,4080 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007
+ * 2008, 2009, 2013 Free Software Foundation, Inc.
+ * Written by Werner Koch.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU General Public License. If you wish to
+ * allow use of your version of this file only under the terms of the
+ * GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* CCID (ChipCardInterfaceDevices) is a specification for accessing
+ smartcard via a reader connected to the USB.
+
+ This is a limited driver allowing to use some CCID drivers directly
+ without any other specila drivers. This is a fallback driver to be
+ used when nothing else works or the system should be kept minimal
+ for security reasons. It makes use of the libusb library to gain
+ portable access to USB.
+
+ This driver has been tested with the SCM SCR335 and SPR532
+ smartcard readers and requires that a reader implements APDU or
+ TPDU level exchange and does fully automatic initialization.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(HAVE_LIBUSB) || defined(TEST)
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef HAVE_NPTH
+# include <npth.h>
+#endif /*HAVE_NPTH*/
+
+#include <libusb.h>
+
+#include "scdaemon.h"
+#include "iso7816.h"
+#define CCID_DRIVER_INCLUDE_USB_IDS 1
+#include "ccid-driver.h"
+
+#define DRVNAME "ccid-driver: "
+
+/* Max length of buffer with out CCID message header of 10-byte
+ Sending: 547 for RSA-4096 key import
+ APDU size = 540 (24+4+256+256)
+ command + lc + le = 4 + 3 + 0
+ Sending: write data object of cardholder certificate
+ APDU size = 2048
+ command + lc + le = 4 + 3 + 0
+ Receiving: 2048 for cardholder certificate
+*/
+#define CCID_MAX_BUF (2048+7+10)
+
+/* CCID command timeout. */
+#define CCID_CMD_TIMEOUT (5*1000)
+
+/* Number of supported devices. See MAX_READER in apdu.c. */
+#define CCID_MAX_DEVICE 4
+
+
+/* Depending on how this source is used we either define our error
+ output to go to stderr or to the GnuPG based logging functions. We
+ use the latter when GNUPG_MAJOR_VERSION or GNUPG_SCD_MAIN_HEADER
+ are defined. */
+#if defined(GNUPG_MAJOR_VERSION) || defined(GNUPG_SCD_MAIN_HEADER)
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+# include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
+# include "options.h"
+# include "util.h"
+# include "memory.h"
+# include "cardglue.h"
+# else /* This is the modularized GnuPG 1.9 or later. */
+# include "scdaemon.h"
+#endif
+
+
+# define DEBUGOUT(t) do { if (debug_level) \
+ log_debug (DRVNAME t); } while (0)
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
+ log_debug (DRVNAME t,(a)); } while (0)
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b)); } while (0)
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b),(c));} while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b),(c),(d));} while (0)
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
+ log_printf (t); } while (0)
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
+ log_printf (t,(a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
+ log_printf (t,(a),(b)); } while (0)
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
+ log_printf (t,(a),(b),(c)); } while (0)
+# define DEBUGOUT_LF() do { if (debug_level) \
+ log_printf ("\n"); } while (0)
+
+#else /* Other usage of this source - don't use gnupg specifics. */
+
+# define DEBUGOUT(t) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t); } while (0)
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a)); } while (0)
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0)
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
+ fprintf (stderr, t); } while (0)
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, t, (a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b)); } while (0)
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_LF() do { if (debug_level) \
+ putc ('\n', stderr); } while (0)
+
+#endif /* This source not used by scdaemon. */
+
+
+#ifndef EAGAIN
+#define EAGAIN EWOULDBLOCK
+#endif
+
+
+
+enum {
+ RDR_to_PC_NotifySlotChange= 0x50,
+ RDR_to_PC_HardwareError = 0x51,
+
+ PC_to_RDR_SetParameters = 0x61,
+ PC_to_RDR_IccPowerOn = 0x62,
+ PC_to_RDR_IccPowerOff = 0x63,
+ PC_to_RDR_GetSlotStatus = 0x65,
+ PC_to_RDR_Secure = 0x69,
+ PC_to_RDR_T0APDU = 0x6a,
+ PC_to_RDR_Escape = 0x6b,
+ PC_to_RDR_GetParameters = 0x6c,
+ PC_to_RDR_ResetParameters = 0x6d,
+ PC_to_RDR_IccClock = 0x6e,
+ PC_to_RDR_XfrBlock = 0x6f,
+ PC_to_RDR_Mechanical = 0x71,
+ PC_to_RDR_Abort = 0x72,
+ PC_to_RDR_SetDataRate = 0x73,
+
+ RDR_to_PC_DataBlock = 0x80,
+ RDR_to_PC_SlotStatus = 0x81,
+ RDR_to_PC_Parameters = 0x82,
+ RDR_to_PC_Escape = 0x83,
+ RDR_to_PC_DataRate = 0x84
+};
+
+
+/* Two macro to detect whether a CCID command has failed and to get
+ the error code. These macros assume that we can access the
+ mandatory first 10 bytes of a CCID message in BUF. */
+#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40)
+#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8])
+
+
+/* Store information on the driver's state. A pointer to such a
+ structure is used as handle for most functions. */
+struct ccid_driver_s
+{
+ libusb_device_handle *idev;
+ unsigned int bai;
+ unsigned short id_vendor;
+ unsigned short id_product;
+ int ifc_no;
+ int ep_bulk_out;
+ int ep_bulk_in;
+ int ep_intr;
+ int seqno;
+ unsigned char t1_ns;
+ unsigned char t1_nr;
+ unsigned char nonnull_nad;
+ int max_ifsd;
+ int max_ccid_msglen;
+ int ifsc;
+ unsigned char apdu_level:2; /* Reader supports short APDU level
+ exchange. With a value of 2 short
+ and extended level is supported.*/
+ unsigned int auto_voltage:1;
+ unsigned int auto_param:1;
+ unsigned int auto_pps:1;
+ unsigned int auto_ifsd:1;
+ unsigned int has_pinpad:2;
+ unsigned int enodev_seen:1;
+ int powered_off;
+
+ time_t last_progress; /* Last time we sent progress line. */
+
+ /* The progress callback and its first arg as supplied to
+ ccid_set_progress_cb. */
+ void (*progress_cb)(void *, const char *, int, int, int);
+ void *progress_cb_arg;
+
+ void (*prompt_cb)(void *, int);
+ void *prompt_cb_arg;
+
+ unsigned char intr_buf[64];
+ struct libusb_transfer *transfer;
+};
+
+
+/* Object to keep infos about found ccid devices. */
+struct ccid_dev_table {
+ int n; /* Index to ccid_usb_dev_list */
+ int interface_number;
+ int setting_number;
+ unsigned char *ifcdesc_extra;
+ int ep_bulk_out;
+ int ep_bulk_in;
+ int ep_intr;
+ size_t ifcdesc_extra_len;
+};
+
+
+static int initialized_usb; /* Tracks whether USB has been initialized. */
+static int debug_level; /* Flag to control the debug output.
+ 0 = No debugging
+ 1 = USB I/O info
+ 2 = Level 1 + T=1 protocol tracing
+ 3 = Level 2 + USB/I/O tracing of SlotStatus.
+ */
+static int ccid_usb_thread_is_alive;
+
+static libusb_device **ccid_usb_dev_list;
+static struct ccid_dev_table ccid_dev_table[CCID_MAX_DEVICE];
+
+
+
+static unsigned int compute_edc (const unsigned char *data, size_t datalen,
+ int use_crc);
+static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+ int no_debug);
+static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
+ size_t *nread, int expected_type, int seqno, int timeout,
+ int no_debug);
+static int abort_cmd (ccid_driver_t handle, int seqno, int init);
+static int send_escape_cmd (ccid_driver_t handle, const unsigned char *data,
+ size_t datalen, unsigned char *result,
+ size_t resultmax, size_t *resultlen);
+
+
+static int
+map_libusb_error (int usberr)
+{
+ switch (usberr)
+ {
+ case 0: return 0;
+ case LIBUSB_ERROR_IO: return CCID_DRIVER_ERR_USB_IO;
+ case LIBUSB_ERROR_ACCESS: return CCID_DRIVER_ERR_USB_ACCESS;
+ case LIBUSB_ERROR_NO_DEVICE:return CCID_DRIVER_ERR_USB_NO_DEVICE;
+ case LIBUSB_ERROR_BUSY: return CCID_DRIVER_ERR_USB_BUSY;
+ case LIBUSB_ERROR_TIMEOUT: return CCID_DRIVER_ERR_USB_TIMEOUT;
+ case LIBUSB_ERROR_OVERFLOW: return CCID_DRIVER_ERR_USB_OVERFLOW;
+ }
+ return CCID_DRIVER_ERR_USB_OTHER;
+}
+
+
+/* Convert a little endian stored 4 byte value into an unsigned
+ integer. */
+static unsigned int
+convert_le_u32 (const unsigned char *buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((unsigned int)buf[3] << 24);
+}
+
+
+/* Convert a little endian stored 2 byte value into an unsigned
+ integer. */
+static unsigned int
+convert_le_u16 (const unsigned char *buf)
+{
+ return buf[0] | (buf[1] << 8);
+}
+
+static void
+set_msg_len (unsigned char *msg, unsigned int length)
+{
+ msg[1] = length;
+ msg[2] = length >> 8;
+ msg[3] = length >> 16;
+ msg[4] = length >> 24;
+}
+
+
+static void
+print_progress (ccid_driver_t handle)
+{
+ time_t ct = time (NULL);
+
+ /* We don't want to print progress lines too often. */
+ if (ct == handle->last_progress)
+ return;
+
+ if (handle->progress_cb)
+ handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0);
+
+ handle->last_progress = ct;
+}
+
+
+
+/* Pint an error message for a failed CCID command including a textual
+ error code. MSG shall be the CCID message at a minimum of 10 bytes. */
+static void
+print_command_failed (const unsigned char *msg)
+{
+ const char *t;
+ char buffer[100];
+ int ec;
+
+ if (!debug_level)
+ return;
+
+ ec = CCID_ERROR_CODE (msg);
+ switch (ec)
+ {
+ case 0x00: t = "Command not supported"; break;
+
+ case 0xE0: t = "Slot busy"; break;
+ case 0xEF: t = "PIN cancelled"; break;
+ case 0xF0: t = "PIN timeout"; break;
+
+ case 0xF2: t = "Automatic sequence ongoing"; break;
+ case 0xF3: t = "Deactivated Protocol"; break;
+ case 0xF4: t = "Procedure byte conflict"; break;
+ case 0xF5: t = "ICC class not supported"; break;
+ case 0xF6: t = "ICC protocol not supported"; break;
+ case 0xF7: t = "Bad checksum in ATR"; break;
+ case 0xF8: t = "Bad TS in ATR"; break;
+
+ case 0xFB: t = "An all inclusive hardware error occurred"; break;
+ case 0xFC: t = "Overrun error while talking to the ICC"; break;
+ case 0xFD: t = "Parity error while talking to the ICC"; break;
+ case 0xFE: t = "CCID timed out while talking to the ICC"; break;
+ case 0xFF: t = "Host aborted the current activity"; break;
+
+ default:
+ if (ec > 0 && ec < 128)
+ sprintf (buffer, "Parameter error at offset %d", ec);
+ else
+ sprintf (buffer, "Error code %02X", ec);
+ t = buffer;
+ break;
+ }
+ DEBUGOUT_1 ("CCID command failed: %s\n", t);
+}
+
+
+static void
+print_pr_data (const unsigned char *data, size_t datalen, size_t off)
+{
+ int any = 0;
+
+ for (; off < datalen; off++)
+ {
+ if (!any || !(off % 16))
+ {
+ if (any)
+ DEBUGOUT_LF ();
+ DEBUGOUT_1 (" [%04lu] ", (unsigned long) off);
+ }
+ DEBUGOUT_CONT_1 (" %02X", data[off]);
+ any = 1;
+ }
+ if (any && (off % 16))
+ DEBUGOUT_LF ();
+}
+
+
+static void
+print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+ DEBUGOUT_1 ("%s:\n", name);
+ if (msglen < 7)
+ return;
+ DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
+ DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]);
+ DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]);
+}
+
+
+static void
+print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_2 (" bPowerSelect ......: 0x%02x (%s)\n", msg[7],
+ msg[7] == 0? "auto":
+ msg[7] == 1? "5.0 V":
+ msg[7] == 2? "3.0 V":
+ msg[7] == 3? "1.8 V":"");
+ print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
+{
+ unsigned int val;
+
+ print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bBWI ..............: 0x%02x\n", msg[7]);
+ val = convert_le_u16 (msg+8);
+ DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val,
+ val == 1? " (continued)":
+ val == 2? " (continues+ends)":
+ val == 3? " (continues+continued)":
+ val == 16? " (DataBlock-expected)":"");
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_getparameters (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setparameters (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bProtocolNum ......: 0x%02x\n", msg[7]);
+ print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_escape (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_iccclock (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bClockCommand .....: 0x%02x\n", msg[7]);
+ print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bmChanges .........: 0x%02x\n", msg[7]);
+ DEBUGOUT_1 (" bClassGetResponse .: 0x%02x\n", msg[8]);
+ DEBUGOUT_1 (" bClassEnvelope ....: 0x%02x\n", msg[9]);
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_secure (const unsigned char *msg, size_t msglen)
+{
+ unsigned int val;
+
+ print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bBMI ..............: 0x%02x\n", msg[7]);
+ val = convert_le_u16 (msg+8);
+ DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val,
+ val == 1? " (continued)":
+ val == 2? " (continues+ends)":
+ val == 3? " (continues+continued)":
+ val == 16? " (DataBlock-expected)":"");
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_p2r_mechanical (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bFunction .........: 0x%02x\n", msg[7]);
+ print_pr_data (msg, msglen, 8);
+}
+
+
+static void
+print_p2r_abort (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
+ if (msglen < 10)
+ return;
+ print_pr_data (msg, msglen, 7);
+}
+
+
+static void
+print_p2r_unknown (const unsigned char *msg, size_t msglen)
+{
+ print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
+ if (msglen < 10)
+ return;
+ print_pr_data (msg, msglen, 0);
+}
+
+
+static void
+print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
+{
+ DEBUGOUT_1 ("%s:\n", name);
+ if (msglen < 9)
+ return;
+ DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
+ DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]);
+ DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]);
+ DEBUGOUT_1 (" bStatus ...........: %u\n", msg[7]);
+ if (msg[8])
+ DEBUGOUT_1 (" bError ............: %u\n", msg[8]);
+}
+
+
+static void
+print_r2p_datablock (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
+ if (msglen < 10)
+ return;
+ if (msg[9])
+ DEBUGOUT_2 (" bChainParameter ...: 0x%02x%s\n", msg[9],
+ msg[9] == 1? " (continued)":
+ msg[9] == 2? " (continues+ends)":
+ msg[9] == 3? " (continues+continued)":
+ msg[9] == 16? " (XferBlock-expected)":"");
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_2 (" bClockStatus ......: 0x%02x%s\n", msg[9],
+ msg[9] == 0? " (running)":
+ msg[9] == 1? " (stopped-L)":
+ msg[9] == 2? " (stopped-H)":
+ msg[9] == 3? " (stopped)":"");
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_parameters (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
+ if (msglen < 10)
+ return;
+
+ DEBUGOUT_1 (" protocol ..........: T=%d\n", msg[9]);
+ if (msglen == 17 && msg[9] == 1)
+ {
+ /* Protocol T=1. */
+ DEBUGOUT_1 (" bmFindexDindex ....: %02X\n", msg[10]);
+ DEBUGOUT_1 (" bmTCCKST1 .........: %02X\n", msg[11]);
+ DEBUGOUT_1 (" bGuardTimeT1 ......: %02X\n", msg[12]);
+ DEBUGOUT_1 (" bmWaitingIntegersT1: %02X\n", msg[13]);
+ DEBUGOUT_1 (" bClockStop ........: %02X\n", msg[14]);
+ DEBUGOUT_1 (" bIFSC .............: %d\n", msg[15]);
+ DEBUGOUT_1 (" bNadValue .........: %d\n", msg[16]);
+ }
+ else
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_escape (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]);
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_datarate (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
+ if (msglen < 10)
+ return;
+ if (msglen >= 18)
+ {
+ DEBUGOUT_1 (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
+ DEBUGOUT_1 (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
+ print_pr_data (msg, msglen, 18);
+ }
+ else
+ print_pr_data (msg, msglen, 10);
+}
+
+
+static void
+print_r2p_unknown (const unsigned char *msg, size_t msglen)
+{
+ print_r2p_header ("Unknown RDR_to_PC command", msg, msglen);
+ if (msglen < 10)
+ return;
+ DEBUGOUT_1 (" bMessageType ......: %02X\n", msg[0]);
+ DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]);
+ print_pr_data (msg, msglen, 10);
+}
+
+
+/* Parse a CCID descriptor, optionally print all available features
+ and test whether this reader is usable by this driver. Returns 0
+ if it is usable.
+
+ Note, that this code is based on the one in lsusb.c of the
+ usb-utils package, I wrote on 2003-09-01. -wk. */
+static int
+parse_ccid_descriptor (ccid_driver_t handle, unsigned short bcd_device,
+ const unsigned char *buf, size_t buflen)
+{
+ unsigned int i;
+ unsigned int us;
+ int have_t1 = 0, have_tpdu=0;
+
+ handle->nonnull_nad = 0;
+ handle->auto_ifsd = 0;
+ handle->max_ifsd = 32;
+ handle->has_pinpad = 0;
+ handle->apdu_level = 0;
+ handle->auto_voltage = 0;
+ handle->auto_param = 0;
+ handle->auto_pps = 0;
+ DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
+ handle->id_vendor, handle->id_product, bcd_device);
+ if (buflen < 54 || buf[0] < 54)
+ {
+ DEBUGOUT ("CCID device descriptor is too short\n");
+ return -1;
+ }
+
+ DEBUGOUT ("ChipCard Interface Descriptor:\n");
+ DEBUGOUT_1 (" bLength %5u\n", buf[0]);
+ DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]);
+ DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]);
+ if (buf[3] != 1 || buf[2] != 0)
+ DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)");
+ DEBUGOUT_LF ();
+
+ DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]);
+ DEBUGOUT_2 (" bVoltageSupport %5u %s\n",
+ buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
+ : buf[5] == 3? "1.8V":"?"));
+
+ us = convert_le_u32 (buf+6);
+ DEBUGOUT_1 (" dwProtocols %5u ", us);
+ if ((us & 1))
+ DEBUGOUT_CONT (" T=0");
+ if ((us & 2))
+ {
+ DEBUGOUT_CONT (" T=1");
+ have_t1 = 1;
+ }
+ if ((us & ~3))
+ DEBUGOUT_CONT (" (Invalid values detected)");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+10);
+ DEBUGOUT_1 (" dwDefaultClock %5u\n", us);
+ us = convert_le_u32(buf+14);
+ DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us);
+ DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]);
+ us = convert_le_u32(buf+19);
+ DEBUGOUT_1 (" dwDataRate %7u bps\n", us);
+ us = convert_le_u32(buf+23);
+ DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us);
+ DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]);
+
+ us = convert_le_u32(buf+28);
+ DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
+ handle->max_ifsd = us;
+
+ us = convert_le_u32(buf+32);
+ DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
+ if ((us&1))
+ DEBUGOUT_CONT ( " 2-wire");
+ if ((us&2))
+ DEBUGOUT_CONT ( " 3-wire");
+ if ((us&4))
+ DEBUGOUT_CONT ( " I2C");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+36);
+ DEBUGOUT_1 (" dwMechanical %08X ", us);
+ if ((us & 1))
+ DEBUGOUT_CONT (" accept");
+ if ((us & 2))
+ DEBUGOUT_CONT (" eject");
+ if ((us & 4))
+ DEBUGOUT_CONT (" capture");
+ if ((us & 8))
+ DEBUGOUT_CONT (" lock");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+40);
+ DEBUGOUT_1 (" dwFeatures %08X\n", us);
+ if ((us & 0x0002))
+ {
+ DEBUGOUT (" Auto configuration based on ATR (assumes auto voltage)\n");
+ handle->auto_voltage = 1;
+ }
+ if ((us & 0x0004))
+ DEBUGOUT (" Auto activation on insert\n");
+ if ((us & 0x0008))
+ {
+ DEBUGOUT (" Auto voltage selection\n");
+ handle->auto_voltage = 1;
+ }
+ if ((us & 0x0010))
+ DEBUGOUT (" Auto clock change\n");
+ if ((us & 0x0020))
+ DEBUGOUT (" Auto baud rate change\n");
+ if ((us & 0x0040))
+ {
+ DEBUGOUT (" Auto parameter negotiation made by CCID\n");
+ handle->auto_param = 1;
+ }
+ else if ((us & 0x0080))
+ {
+ DEBUGOUT (" Auto PPS made by CCID\n");
+ handle->auto_pps = 1;
+ }
+ if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
+ DEBUGOUT (" WARNING: conflicting negotiation features\n");
+
+ if ((us & 0x0100))
+ DEBUGOUT (" CCID can set ICC in clock stop mode\n");
+ if ((us & 0x0200))
+ {
+ DEBUGOUT (" NAD value other than 0x00 accepted\n");
+ handle->nonnull_nad = 1;
+ }
+ if ((us & 0x0400))
+ {
+ DEBUGOUT (" Auto IFSD exchange\n");
+ handle->auto_ifsd = 1;
+ }
+
+ if ((us & 0x00010000))
+ {
+ DEBUGOUT (" TPDU level exchange\n");
+ have_tpdu = 1;
+ }
+ else if ((us & 0x00020000))
+ {
+ DEBUGOUT (" Short APDU level exchange\n");
+ handle->apdu_level = 1;
+ }
+ else if ((us & 0x00040000))
+ {
+ DEBUGOUT (" Short and extended APDU level exchange\n");
+ handle->apdu_level = 2;
+ }
+ else if ((us & 0x00070000))
+ DEBUGOUT (" WARNING: conflicting exchange levels\n");
+
+ us = convert_le_u32(buf+44);
+ DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
+ handle->max_ccid_msglen = us;
+
+ DEBUGOUT ( " bClassGetResponse ");
+ if (buf[48] == 0xff)
+ DEBUGOUT_CONT ("echo\n");
+ else
+ DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
+
+ DEBUGOUT ( " bClassEnvelope ");
+ if (buf[49] == 0xff)
+ DEBUGOUT_CONT ("echo\n");
+ else
+ DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
+
+ DEBUGOUT ( " wlcdLayout ");
+ if (!buf[50] && !buf[51])
+ DEBUGOUT_CONT ("none\n");
+ else
+ DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);
+
+ DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
+ if ((buf[52] & 1))
+ {
+ DEBUGOUT_CONT ( " verification");
+ handle->has_pinpad |= 1;
+ }
+ if ((buf[52] & 2))
+ {
+ DEBUGOUT_CONT ( " modification");
+ handle->has_pinpad |= 2;
+ }
+ DEBUGOUT_LF ();
+
+ DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
+
+ if (buf[0] > 54)
+ {
+ DEBUGOUT (" junk ");
+ for (i=54; i < buf[0]-54; i++)
+ DEBUGOUT_CONT_1 (" %02X", buf[i]);
+ DEBUGOUT_LF ();
+ }
+
+ if (!have_t1 || !(have_tpdu || handle->apdu_level))
+ {
+ DEBUGOUT ("this drivers requires that the reader supports T=1, "
+ "TPDU or APDU level exchange - this is not available\n");
+ return -1;
+ }
+
+
+ /* SCM drivers get stuck in their internal USB stack if they try to
+ send a frame of n*wMaxPacketSize back to us. Given that
+ wMaxPacketSize is 64 for these readers we set the IFSD to a value
+ lower than that:
+ 64 - 10 CCID header - 4 T1frame - 2 reserved = 48
+ Product Ids:
+ 0xe001 - SCR 331
+ 0x5111 - SCR 331-DI
+ 0x5115 - SCR 335
+ 0xe003 - SPR 532
+ The
+ 0x5117 - SCR 3320 USB ID-000 reader
+ seems to be very slow but enabling this workaround boosts the
+ performance to a more or less acceptable level (tested by David).
+
+ */
+ if (handle->id_vendor == VENDOR_SCM
+ && handle->max_ifsd > 48
+ && ( (handle->id_product == SCM_SCR331 && bcd_device < 0x0516)
+ ||(handle->id_product == SCM_SCR331DI && bcd_device < 0x0620)
+ ||(handle->id_product == SCM_SCR335 && bcd_device < 0x0514)
+ ||(handle->id_product == SCM_SPR532 && bcd_device < 0x0504)
+ ||(handle->id_product == SCM_SCR3320 && bcd_device < 0x0522)
+ ))
+ {
+ DEBUGOUT ("enabling workaround for buggy SCM readers\n");
+ handle->max_ifsd = 48;
+ }
+
+ if (handle->id_vendor == VENDOR_GEMPC)
+ {
+ DEBUGOUT ("enabling product quirk: disable non-null NAD\n");
+ handle->nonnull_nad = 0;
+ }
+
+ return 0;
+}
+
+
+static char *
+get_escaped_usb_string (libusb_device_handle *idev, int idx,
+ const char *prefix, const char *suffix)
+{
+ int rc;
+ unsigned char buf[280];
+ unsigned char *s;
+ unsigned int langid;
+ size_t i, n, len;
+ char *result;
+
+ if (!idx)
+ return NULL;
+
+ /* Fixme: The next line is for the current Valgrid without support
+ for USB IOCTLs. */
+ memset (buf, 0, sizeof buf);
+
+ /* First get the list of supported languages and use the first one.
+ If we do don't find it we try to use English. Note that this is
+ all in a 2 bute Unicode encoding using little endian. */
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_DESCRIPTOR,
+ (LIBUSB_DT_STRING << 8), 0,
+ buf, sizeof buf, 1000 /* ms timeout */);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc < 4)
+ langid = 0x0409; /* English. */
+ else
+ langid = (buf[3] << 8) | buf[2];
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN,
+ LIBUSB_REQUEST_GET_DESCRIPTOR,
+ (LIBUSB_DT_STRING << 8) + idx, langid,
+ buf, sizeof buf, 1000 /* ms timeout */);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc < 2 || buf[1] != LIBUSB_DT_STRING)
+ return NULL; /* Error or not a string. */
+ len = buf[0];
+ if (len > rc)
+ return NULL; /* Larger than our buffer. */
+
+ for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ n++; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ n += 3 ;
+ else
+ n++;
+ }
+
+ result = malloc (strlen (prefix) + n + strlen (suffix) + 1);
+ if (!result)
+ return NULL;
+
+ strcpy (result, prefix);
+ n = strlen (prefix);
+ for (s=buf+2, i=2; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ result[n++] = '\xff'; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ {
+ sprintf (result+n, "%%%02X", *s);
+ n += 3;
+ }
+ else
+ result[n++] = *s;
+ }
+ strcpy (result+n, suffix);
+
+ return result;
+}
+
+/* This function creates an reader id to be used to find the same
+ physical reader after a reset. It returns an allocated and possibly
+ percent escaped string or NULL if not enough memory is available. */
+static char *
+make_reader_id (libusb_device_handle *idev,
+ unsigned int vendor, unsigned int product,
+ unsigned char serialno_index)
+{
+ char *rid;
+ char prefix[20];
+
+ sprintf (prefix, "%04X:%04X:", (vendor & 0xffff), (product & 0xffff));
+ rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0");
+ if (!rid)
+ {
+ rid = malloc (strlen (prefix) + 3 + 1);
+ if (!rid)
+ return NULL;
+ strcpy (rid, prefix);
+ strcat (rid, "X:0");
+ }
+ return rid;
+}
+
+
+/* Helper to find the endpoint from an interface descriptor. */
+static int
+find_endpoint (const struct libusb_interface_descriptor *ifcdesc, int mode)
+{
+ int no;
+ int want_bulk_in = 0;
+
+ if (mode == 1)
+ want_bulk_in = 0x80;
+ for (no=0; no < ifcdesc->bNumEndpoints; no++)
+ {
+ const struct libusb_endpoint_descriptor *ep = ifcdesc->endpoint + no;
+ if (ep->bDescriptorType != LIBUSB_DT_ENDPOINT)
+ ;
+ else if (mode == 2
+ && ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
+ == LIBUSB_TRANSFER_TYPE_INTERRUPT)
+ && (ep->bEndpointAddress & 0x80))
+ return ep->bEndpointAddress;
+ else if ((mode == 0 || mode == 1)
+ && ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
+ == LIBUSB_TRANSFER_TYPE_BULK)
+ && (ep->bEndpointAddress & 0x80) == want_bulk_in)
+ return ep->bEndpointAddress;
+ }
+
+ return -1;
+}
+
+
+/* Helper for scan_devices. This function returns true if a
+ requested device has been found or the caller should stop scanning
+ for other reasons. */
+static void
+scan_usb_device (int *count, char **rid_list, struct libusb_device *dev)
+{
+ int ifc_no;
+ int set_no;
+ const struct libusb_interface_descriptor *ifcdesc;
+ char *rid;
+ libusb_device_handle *idev = NULL;
+ int err;
+ struct libusb_config_descriptor *config;
+ struct libusb_device_descriptor desc;
+ char *p;
+
+ err = libusb_get_device_descriptor (dev, &desc);
+ if (err)
+ return;
+
+ err = libusb_get_active_config_descriptor (dev, &config);
+ if (err)
+ return;
+
+ for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+ for (set_no=0; set_no < config->interface[ifc_no].num_altsetting; set_no++)
+ {
+ ifcdesc = (config->interface[ifc_no].altsetting + set_no);
+ /* The second condition is for older SCM SPR 532 who did
+ not know about the assigned CCID class. The third
+ condition does the same for a Cherry SmartTerminal
+ ST-2000. Instead of trying to interpret the strings
+ we simply check the product ID. */
+ if (ifcdesc && ifcdesc->extra
+ && ((ifcdesc->bInterfaceClass == 11
+ && ifcdesc->bInterfaceSubClass == 0
+ && ifcdesc->bInterfaceProtocol == 0)
+ || (ifcdesc->bInterfaceClass == 255
+ && desc.idVendor == VENDOR_SCM
+ && desc.idProduct == SCM_SPR532)
+ || (ifcdesc->bInterfaceClass == 255
+ && desc.idVendor == VENDOR_CHERRY
+ && desc.idProduct == CHERRY_ST2000)))
+ {
+ ++*count;
+
+ err = libusb_open (dev, &idev);
+ if (err)
+ {
+ DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (err));
+ continue; /* with next setting. */
+ }
+
+ rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
+ desc.iSerialNumber);
+ if (!rid)
+ {
+ libusb_free_config_descriptor (config);
+ return;
+ }
+
+ /* We are collecting infos about all available CCID
+ readers. Store them and continue. */
+ DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", *count, rid);
+ p = malloc ((*rid_list? strlen (*rid_list):0) + 1
+ + strlen (rid) + 1);
+ if (p)
+ {
+ *p = 0;
+ if (*rid_list)
+ {
+ strcat (p, *rid_list);
+ free (*rid_list);
+ }
+ strcat (p, rid);
+ strcat (p, "\n");
+ *rid_list = p;
+ }
+ else /* Out of memory. */
+ {
+ libusb_free_config_descriptor (config);
+ free (rid);
+ return;
+ }
+
+ free (rid);
+ libusb_close (idev);
+ idev = NULL;
+ }
+ }
+
+ libusb_free_config_descriptor (config);
+}
+
+/* Scan all CCID devices.
+
+ The function returns 0 if a reader has been found or when a scan
+ returned without error.
+
+ R_RID should be the address where to store the list of reader_ids
+ we found. If on return this list is empty, no CCID device has been
+ found; otherwise it points to an allocated linked list of reader
+ IDs.
+*/
+static int
+scan_devices (char **r_rid)
+{
+ char *rid_list = NULL;
+ int count = 0;
+ libusb_device **dev_list = NULL;
+ libusb_device *dev;
+ int i;
+ ssize_t n;
+
+ /* Set return values to a default. */
+ if (r_rid)
+ *r_rid = NULL;
+
+ n = libusb_get_device_list (NULL, &dev_list);
+
+ for (i = 0; i < n; i++)
+ {
+ dev = dev_list[i];
+ scan_usb_device (&count, &rid_list, dev);
+ }
+
+ libusb_free_device_list (dev_list, 1);
+
+ *r_rid = rid_list;
+ return 0;
+}
+
+
+/* Set the level of debugging to LEVEL and return the old level. -1
+ just returns the old level. A level of 0 disables debugging, 1
+ enables debugging, 2 enables additional tracing of the T=1
+ protocol, 3 additionally enables debugging for GetSlotStatus, other
+ values are not yet defined.
+
+ Note that libusb may provide its own debugging feature which is
+ enabled by setting the envvar USB_DEBUG. */
+int
+ccid_set_debug_level (int level)
+{
+ int old = debug_level;
+ if (level != -1)
+ debug_level = level;
+ return old;
+}
+
+
+char *
+ccid_get_reader_list (void)
+{
+ char *reader_list;
+
+ if (!initialized_usb)
+ {
+ int rc;
+ if ((rc = libusb_init (NULL)))
+ {
+ DEBUGOUT_1 ("usb_init failed: %s.\n", libusb_error_name (rc));
+ return NULL;
+ }
+ initialized_usb = 1;
+ }
+
+ if (scan_devices (&reader_list))
+ return NULL; /* Error. */
+ return reader_list;
+}
+
+
+/* Vendor specific custom initialization. */
+static int
+ccid_vendor_specific_init (ccid_driver_t handle)
+{
+ int r = 0;
+
+ if (handle->id_vendor == VENDOR_VEGA && handle->id_product == VEGA_ALPHA)
+ {
+ /*
+ * Vega alpha has a feature to show retry counter on the pinpad
+ * display. But it assumes that the card returns the value of
+ * retry counter by VERIFY with empty data (return code of
+ * 63Cx). Unfortunately, existing OpenPGP cards don't support
+ * VERIFY command with empty data. This vendor specific command
+ * sequence is to disable the feature.
+ */
+ const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
+
+ r = send_escape_cmd (handle, cmd, sizeof (cmd), NULL, 0, NULL);
+ }
+ else if (handle->id_vendor == VENDOR_SCM && handle->id_product == SCM_SPR532)
+ {
+ /*
+ * It seems that SEQ may be out of sync between host and the card reader,
+ * and SET_INTERFACE doesn't reset it. Make sure it works at the init.
+ */
+ abort_cmd (handle, 0, 1);
+ }
+
+ if (r != 0 && r != CCID_DRIVER_ERR_CARD_INACTIVE
+ && r != CCID_DRIVER_ERR_NO_CARD)
+ return r;
+ else
+ return 0;
+}
+
+
+static int
+ccid_vendor_specific_setup (ccid_driver_t handle)
+{
+ if (handle->id_vendor == VENDOR_SCM && handle->id_product == SCM_SPR532)
+ {
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_clear_halt (handle->idev, handle->ep_intr);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ }
+ return 0;
+}
+
+
+static int
+ccid_vendor_specific_pinpad_setup (ccid_driver_t handle)
+{
+ if (handle->id_vendor == VENDOR_SCM && handle->id_product == SCM_SPR532)
+ {
+ DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n");
+ send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3,
+ NULL, 0, NULL);
+ }
+ return 0;
+}
+
+
+gpg_error_t
+ccid_dev_scan (int *idx_max_p, void **t_p)
+{
+ ssize_t n;
+ libusb_device *dev;
+ int i;
+ int ifc_no;
+ int set_no;
+ int idx = 0;
+ int err = 0;
+
+ *idx_max_p = 0;
+ *t_p = NULL;
+
+ if (!initialized_usb)
+ {
+ int rc;
+ if ((rc = libusb_init (NULL)))
+ {
+ DEBUGOUT_1 ("usb_init failed: %s.\n", libusb_error_name (rc));
+ return gpg_error (GPG_ERR_ENODEV);
+ }
+ initialized_usb = 1;
+ }
+
+ n = libusb_get_device_list (NULL, &ccid_usb_dev_list);
+ for (i = 0; i < n; i++)
+ {
+ struct libusb_config_descriptor *config;
+ struct libusb_device_descriptor desc;
+
+ dev = ccid_usb_dev_list[i];
+
+ if (libusb_get_device_descriptor (dev, &desc))
+ continue;
+
+ if (libusb_get_active_config_descriptor (dev, &config))
+ continue;
+
+ for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+ for (set_no=0; set_no < config->interface[ifc_no].num_altsetting;
+ set_no++)
+ {
+ const struct libusb_interface_descriptor *ifcdesc;
+
+ ifcdesc = &config->interface[ifc_no].altsetting[set_no];
+ /* The second condition is for older SCM SPR 532 who did
+ not know about the assigned CCID class. The third
+ condition does the same for a Cherry SmartTerminal
+ ST-2000. Instead of trying to interpret the strings
+ we simply check the product ID. */
+ if (ifcdesc && ifcdesc->extra
+ && ((ifcdesc->bInterfaceClass == 11
+ && ifcdesc->bInterfaceSubClass == 0
+ && ifcdesc->bInterfaceProtocol == 0)
+ || (ifcdesc->bInterfaceClass == 255
+ && desc.idVendor == VENDOR_SCM
+ && desc.idProduct == SCM_SPR532)
+ || (ifcdesc->bInterfaceClass == 255
+ && desc.idVendor == VENDOR_CHERRY
+ && desc.idProduct == CHERRY_ST2000)))
+ {
+ /* Found a reader. */
+ unsigned char *ifcdesc_extra;
+
+ ifcdesc_extra = malloc (ifcdesc->extra_length);
+ if (!ifcdesc_extra)
+ {
+ err = gpg_error_from_syserror ();
+ libusb_free_config_descriptor (config);
+ goto scan_finish;
+ }
+ memcpy (ifcdesc_extra, ifcdesc->extra, ifcdesc->extra_length);
+
+ ccid_dev_table[idx].n = i;
+ ccid_dev_table[idx].interface_number = ifc_no;
+ ccid_dev_table[idx].setting_number = set_no;
+ ccid_dev_table[idx].ifcdesc_extra = ifcdesc_extra;
+ ccid_dev_table[idx].ifcdesc_extra_len = ifcdesc->extra_length;
+ ccid_dev_table[idx].ep_bulk_out = find_endpoint (ifcdesc, 0);
+ ccid_dev_table[idx].ep_bulk_in = find_endpoint (ifcdesc, 1);
+ ccid_dev_table[idx].ep_intr = find_endpoint (ifcdesc, 2);
+
+ idx++;
+ if (idx >= CCID_MAX_DEVICE)
+ {
+ libusb_free_config_descriptor (config);
+ err = 0;
+ goto scan_finish;
+ }
+ }
+ }
+
+ libusb_free_config_descriptor (config);
+ }
+
+ scan_finish:
+
+ if (err)
+ {
+ for (i = 0; i < idx; i++)
+ {
+ free (ccid_dev_table[i].ifcdesc_extra);
+ ccid_dev_table[i].n = 0;
+ ccid_dev_table[i].interface_number = 0;
+ ccid_dev_table[i].setting_number = 0;
+ ccid_dev_table[i].ifcdesc_extra = NULL;
+ ccid_dev_table[i].ifcdesc_extra_len = 0;
+ ccid_dev_table[i].ep_bulk_out = 0;
+ ccid_dev_table[i].ep_bulk_in = 0;
+ ccid_dev_table[i].ep_intr = 0;
+ }
+ libusb_free_device_list (ccid_usb_dev_list, 1);
+ ccid_usb_dev_list = NULL;
+ }
+ else
+ {
+ *idx_max_p = idx;
+ if (idx)
+ *t_p = ccid_dev_table;
+ else
+ *t_p = NULL;
+ }
+
+ return err;
+}
+
+void
+ccid_dev_scan_finish (void *tbl0, int max)
+{
+ int i;
+ struct ccid_dev_table *tbl = tbl0;
+
+ for (i = 0; i < max; i++)
+ {
+ free (tbl[i].ifcdesc_extra);
+ tbl[i].n = 0;
+ tbl[i].interface_number = 0;
+ tbl[i].setting_number = 0;
+ tbl[i].ifcdesc_extra = NULL;
+ tbl[i].ifcdesc_extra_len = 0;
+ tbl[i].ep_bulk_out = 0;
+ tbl[i].ep_bulk_in = 0;
+ tbl[i].ep_intr = 0;
+ }
+ libusb_free_device_list (ccid_usb_dev_list, 1);
+ ccid_usb_dev_list = NULL;
+}
+
+unsigned int
+ccid_get_BAI (int idx, void *tbl0)
+{
+ int n;
+ int bus, addr, intf;
+ unsigned int bai;
+ libusb_device *dev;
+ struct ccid_dev_table *tbl = tbl0;
+
+ n = tbl[idx].n;
+ dev = ccid_usb_dev_list[n];
+
+ bus = libusb_get_bus_number (dev);
+ addr = libusb_get_device_address (dev);
+ intf = tbl[idx].interface_number;
+ bai = (bus << 16) | (addr << 8) | intf;
+
+ return bai;
+}
+
+int
+ccid_compare_BAI (ccid_driver_t handle, unsigned int bai)
+{
+ return handle->bai == bai;
+}
+
+
+static void
+intr_cb (struct libusb_transfer *transfer)
+{
+ ccid_driver_t handle = transfer->user_data;
+
+ DEBUGOUT_2 ("CCID: interrupt callback %d (%d)\n",
+ transfer->status, transfer->actual_length);
+
+ if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT)
+ {
+ int err;
+
+ submit_again:
+ /* Submit the URB again to keep watching the INTERRUPT transfer. */
+ err = libusb_submit_transfer (transfer);
+ if (err == LIBUSB_ERROR_NO_DEVICE)
+ goto device_removed;
+
+ DEBUGOUT_1 ("CCID submit transfer again %d\n", err);
+ }
+ else if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
+ {
+ size_t len = transfer->actual_length;
+ unsigned char *p = transfer->buffer;
+ int card_removed = 0;
+
+ while (len)
+ {
+ if (*p == RDR_to_PC_NotifySlotChange)
+ {
+ if (len < 2)
+ break;
+
+ DEBUGOUT_1 ("CCID: NotifySlotChange: %02x\n", p[1]);
+
+ if ((p[1] & 1))
+ card_removed = 0;
+ else
+ card_removed = 1;
+
+ p += 2;
+ len -= 2;
+ }
+ else if (*p == RDR_to_PC_HardwareError)
+ {
+ if (len < 4)
+ break;
+
+ DEBUGOUT_1 ("CCID: hardware error detected: %02x\n", p[3]);
+ p += 4;
+ len -= 4;
+ }
+ else
+ {
+ DEBUGOUT_1 ("CCID: unknown intr: %02x\n", p[0]);
+ break;
+ }
+ }
+
+ if (card_removed)
+ {
+ DEBUGOUT ("CCID: card removed\n");
+ handle->powered_off = 1;
+#if defined(GNUPG_MAJOR_VERSION)
+ scd_kick_the_loop ();
+#endif
+ }
+ else
+ {
+ /* Event other than card removal. */
+ goto submit_again;
+ }
+ }
+ else if (transfer->status == LIBUSB_TRANSFER_CANCELLED)
+ handle->powered_off = 1;
+ else if (transfer->status == LIBUSB_TRANSFER_OVERFLOW)
+ {
+ /* Something goes wrong. Ignore. */
+ DEBUGOUT ("CCID: interrupt transfer overflow\n");
+ }
+ else
+ {
+ device_removed:
+ DEBUGOUT ("CCID: device removed\n");
+ handle->powered_off = 1;
+#if defined(GNUPG_MAJOR_VERSION)
+ scd_kick_the_loop ();
+#endif
+ }
+}
+
+static void
+ccid_setup_intr (ccid_driver_t handle)
+{
+ struct libusb_transfer *transfer;
+ int err;
+
+ transfer = libusb_alloc_transfer (0);
+ handle->transfer = transfer;
+ libusb_fill_interrupt_transfer (transfer, handle->idev, handle->ep_intr,
+ handle->intr_buf, sizeof (handle->intr_buf),
+ intr_cb, handle, 0);
+ err = libusb_submit_transfer (transfer);
+ DEBUGOUT_2 ("CCID submit transfer (%x): %d", handle->ep_intr, err);
+}
+
+
+static void *
+ccid_usb_thread (void *arg)
+{
+ libusb_context *ctx = arg;
+
+ while (ccid_usb_thread_is_alive)
+ {
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_handle_events_completed (ctx, NULL);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ }
+
+ return NULL;
+}
+
+
+static int
+ccid_open_usb_reader (const char *spec_reader_name,
+ int idx, void *ccid_table0,
+ ccid_driver_t *handle, char **rdrname_p)
+{
+ libusb_device *dev;
+ libusb_device_handle *idev = NULL;
+ char *rid = NULL;
+ int rc = 0;
+ int ifc_no, set_no;
+ struct libusb_device_descriptor desc;
+ int n;
+ int bus, addr;
+ unsigned int bai;
+ struct ccid_dev_table *ccid_table = ccid_table0;
+
+ n = ccid_table[idx].n;
+ ifc_no = ccid_table[idx].interface_number;
+ set_no = ccid_table[idx].setting_number;
+
+ dev = ccid_usb_dev_list[n];
+ bus = libusb_get_bus_number (dev);
+ addr = libusb_get_device_address (dev);
+ bai = (bus << 16) | (addr << 8) | ifc_no;
+
+ rc = libusb_open (dev, &idev);
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (rc));
+ free (*handle);
+ *handle = NULL;
+ return map_libusb_error (rc);
+ }
+
+ if (ccid_usb_thread_is_alive++ == 0)
+ {
+ npth_t thread;
+ npth_attr_t tattr;
+ int err;
+
+ err = npth_attr_init (&tattr);
+ if (err)
+ {
+ DEBUGOUT_1 ("npth_attr_init failed: %s\n", strerror (err));
+ free (*handle);
+ *handle = NULL;
+ return err;
+ }
+
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+ err = npth_create (&thread, &tattr, ccid_usb_thread, NULL);
+ if (err)
+ {
+ DEBUGOUT_1 ("npth_create failed: %s\n", strerror (err));
+ free (*handle);
+ *handle = NULL;
+ return err;
+ }
+ npth_setname_np (thread, "ccid_usb_thread");
+
+ npth_attr_destroy (&tattr);
+ }
+
+ rc = libusb_get_device_descriptor (dev, &desc);
+ if (rc)
+ {
+ DEBUGOUT ("get_device_descripor failed\n");
+ rc = map_libusb_error (rc);
+ goto leave;
+ }
+
+ rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
+ desc.iSerialNumber);
+
+ /* Check to see if reader name matches the spec. */
+ if (spec_reader_name
+ && strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
+ {
+ DEBUGOUT ("device not matched\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+ (*handle)->id_vendor = desc.idVendor;
+ (*handle)->id_product = desc.idProduct;
+ (*handle)->idev = idev;
+ (*handle)->bai = bai;
+ (*handle)->ifc_no = ifc_no;
+ (*handle)->ep_bulk_out = ccid_table[idx].ep_bulk_out;
+ (*handle)->ep_bulk_in = ccid_table[idx].ep_bulk_in;
+ (*handle)->ep_intr = ccid_table[idx].ep_intr;
+
+ DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", idx, rid);
+
+ if (parse_ccid_descriptor (*handle, desc.bcdDevice,
+ ccid_table[idx].ifcdesc_extra,
+ ccid_table[idx].ifcdesc_extra_len))
+ {
+ DEBUGOUT ("device not supported\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_claim_interface (idev, ifc_no);
+ if (rc)
+ {
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ rc = map_libusb_error (rc);
+ goto leave;
+ }
+
+ /* Submit SET_INTERFACE control transfer which can reset the device. */
+ rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
+ if (rc)
+ {
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
+ rc = map_libusb_error (rc);
+ goto leave;
+ }
+
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+
+ rc = ccid_vendor_specific_init (*handle);
+
+ leave:
+ if (rc)
+ {
+ --ccid_usb_thread_is_alive;
+ free (rid);
+ libusb_release_interface (idev, ifc_no);
+ libusb_close (idev);
+ free (*handle);
+ *handle = NULL;
+ }
+ else
+ {
+ if (rdrname_p)
+ *rdrname_p = rid;
+ else
+ free (rid);
+ }
+
+ return rc;
+}
+
+/* Open the reader with the internal number READERNO and return a
+ pointer to be used as handle in HANDLE. Returns 0 on success. */
+int
+ccid_open_reader (const char *spec_reader_name, int idx,
+ void *ccid_table0,
+ ccid_driver_t *handle, char **rdrname_p)
+{
+ struct ccid_dev_table *ccid_table = ccid_table0;
+
+ *handle = calloc (1, sizeof **handle);
+ if (!*handle)
+ {
+ DEBUGOUT ("out of memory\n");
+ return CCID_DRIVER_ERR_OUT_OF_CORE;
+ }
+
+ return ccid_open_usb_reader (spec_reader_name, idx, ccid_table,
+ handle, rdrname_p);
+}
+
+
+int
+ccid_require_get_status (ccid_driver_t handle)
+{
+ /* When a card reader supports interrupt transfer to check the
+ status of card, it is possible to submit only an interrupt
+ transfer, and no check is required by application layer. USB can
+ detect removal of a card and can detect removal of a reader.
+ */
+ if (handle->ep_intr >= 0)
+ {
+ if (handle->id_vendor != VENDOR_SCM)
+ return 0;
+
+ /*
+ * For card reader with interrupt transfer support, ideally,
+ * removal is detected by intr_cb, but some card reader
+ * (e.g. SPR532) has a possible case of missing report to
+ * intr_cb, and another case of valid report to intr_cb.
+ *
+ * For such a reader, the removal should be able to be detected
+ * by PC_to_RDR_GetSlotStatus, too. Thus, calls to
+ * ccid_slot_status should go on wire even if "on_wire" is not
+ * requested.
+ *
+ */
+ if (handle->transfer == NULL)
+ return 0;
+ }
+
+ /* Libusb actually detects the removal of USB device in use.
+ However, there is no good API to handle the removal (yet),
+ cleanly and with good portability.
+
+ There is libusb_set_pollfd_notifiers function, but it doesn't
+ offer libusb_device_handle* data to its callback. So, when it
+ watches multiple devices, there is no way to know which device is
+ removed.
+
+ Once, we will have a good programming interface of libusb, we can
+ list tokens (with no interrupt transfer support, but always with
+ card inserted) here to return 0, so that scdaemon can submit
+ minimum packet on wire.
+ */
+ return 1;
+}
+
+static int
+send_power_off (ccid_driver_t handle)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+
+ msg[0] = PC_to_RDR_IccPowerOff;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (!rc)
+ bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
+ seqno, 2000, 0);
+ return rc;
+}
+
+static void
+do_close_reader (ccid_driver_t handle)
+{
+ int rc;
+
+ if (!handle->powered_off)
+ send_power_off (handle);
+
+ if (handle->transfer)
+ {
+ if (!handle->powered_off)
+ {
+ DEBUGOUT ("libusb_cancel_transfer\n");
+
+ rc = libusb_cancel_transfer (handle->transfer);
+ if (rc != LIBUSB_ERROR_NOT_FOUND)
+ while (!handle->powered_off)
+ {
+ DEBUGOUT ("libusb_handle_events_completed\n");
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_handle_events_completed (NULL, &handle->powered_off);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ }
+ }
+
+ libusb_free_transfer (handle->transfer);
+ handle->transfer = NULL;
+ }
+
+ DEBUGOUT ("libusb_release_interface and libusb_close\n");
+ libusb_release_interface (handle->idev, handle->ifc_no);
+ --ccid_usb_thread_is_alive;
+ libusb_close (handle->idev);
+ handle->idev = NULL;
+}
+
+
+int
+ccid_set_progress_cb (ccid_driver_t handle,
+ void (*cb)(void *, const char *, int, int, int),
+ void *cb_arg)
+{
+ if (!handle)
+ return CCID_DRIVER_ERR_INV_VALUE;
+
+ handle->progress_cb = cb;
+ handle->progress_cb_arg = cb_arg;
+ return 0;
+}
+
+
+int
+ccid_set_prompt_cb (ccid_driver_t handle,
+ void (*cb)(void *, int), void *cb_arg)
+{
+ if (!handle)
+ return CCID_DRIVER_ERR_INV_VALUE;
+
+ handle->prompt_cb = cb;
+ handle->prompt_cb_arg = cb_arg;
+ return 0;
+}
+
+
+/* Close the reader HANDLE. */
+int
+ccid_close_reader (ccid_driver_t handle)
+{
+ if (!handle)
+ return 0;
+
+ do_close_reader (handle);
+ free (handle);
+ return 0;
+}
+
+
+/* Return False if a card is present and powered. */
+int
+ccid_check_card_presence (ccid_driver_t handle)
+{
+ (void)handle; /* Not yet implemented. */
+ return -1;
+}
+
+
+/* Write a MSG of length MSGLEN to the designated bulk out endpoint.
+ Returns 0 on success. */
+static int
+bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
+ int no_debug)
+{
+ int rc;
+ int transferred;
+
+ /* No need to continue and clutter the log with USB write error
+ messages after we got the first ENODEV. */
+ if (handle->enodev_seen)
+ return CCID_DRIVER_ERR_NO_READER;
+
+ if (debug_level && (!no_debug || debug_level >= 3))
+ {
+ switch (msglen? msg[0]:0)
+ {
+ case PC_to_RDR_IccPowerOn:
+ print_p2r_iccpoweron (msg, msglen);
+ break;
+ case PC_to_RDR_IccPowerOff:
+ print_p2r_iccpoweroff (msg, msglen);
+ break;
+ case PC_to_RDR_GetSlotStatus:
+ print_p2r_getslotstatus (msg, msglen);
+ break;
+ case PC_to_RDR_XfrBlock:
+ print_p2r_xfrblock (msg, msglen);
+ break;
+ case PC_to_RDR_GetParameters:
+ print_p2r_getparameters (msg, msglen);
+ break;
+ case PC_to_RDR_ResetParameters:
+ print_p2r_resetparameters (msg, msglen);
+ break;
+ case PC_to_RDR_SetParameters:
+ print_p2r_setparameters (msg, msglen);
+ break;
+ case PC_to_RDR_Escape:
+ print_p2r_escape (msg, msglen);
+ break;
+ case PC_to_RDR_IccClock:
+ print_p2r_iccclock (msg, msglen);
+ break;
+ case PC_to_RDR_T0APDU:
+ print_p2r_to0apdu (msg, msglen);
+ break;
+ case PC_to_RDR_Secure:
+ print_p2r_secure (msg, msglen);
+ break;
+ case PC_to_RDR_Mechanical:
+ print_p2r_mechanical (msg, msglen);
+ break;
+ case PC_to_RDR_Abort:
+ print_p2r_abort (msg, msglen);
+ break;
+ case PC_to_RDR_SetDataRate:
+ print_p2r_setdatarate (msg, msglen);
+ break;
+ default:
+ print_p2r_unknown (msg, msglen);
+ break;
+ }
+ }
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out,
+ msg, msglen, &transferred,
+ 5000 /* ms timeout */);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc == 0 && transferred == msglen)
+ return 0;
+
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_bulk_write error: %s\n", libusb_error_name (rc));
+ if (rc == LIBUSB_ERROR_NO_DEVICE)
+ {
+ handle->enodev_seen = 1;
+ return CCID_DRIVER_ERR_NO_READER;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Read a maximum of LENGTH bytes from the bulk in endpoint into
+ BUFFER and return the actual read number if bytes in NREAD. SEQNO
+ is the sequence number used to send the request and EXPECTED_TYPE
+ the type of message we expect. Does checks on the ccid
+ header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to
+ avoid debug messages in case of no error; this can be overriden
+ with a glibal debug level of at least 3. Returns 0 on success. */
+static int
+bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
+ size_t *nread, int expected_type, int seqno, int timeout,
+ int no_debug)
+{
+ int rc;
+ int msglen;
+ int notified = 0;
+ int bwi = 1;
+
+ /* Fixme: The next line for the current Valgrind without support
+ for USB IOCTLs. */
+ memset (buffer, 0, length);
+ retry:
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in,
+ buffer, length, &msglen, bwi*timeout);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_bulk_read error: %s\n", libusb_error_name (rc));
+ if (rc == LIBUSB_ERROR_NO_DEVICE)
+ handle->enodev_seen = 1;
+
+ return map_libusb_error (rc);
+ }
+ if (msglen < 0)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Faulty libusb. */
+ *nread = msglen;
+
+ if (msglen < 10)
+ {
+ DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
+ abort_cmd (handle, seqno, 0);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (buffer[5] != 0)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (buffer[6] != seqno)
+ {
+ DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
+ seqno, buffer[6]);
+ /* Retry until we are synced again. */
+ goto retry;
+ }
+
+ /* We need to handle the time extension request before we check that
+ we got the expected message type. This is in particular required
+ for the Cherry keyboard which sends a time extension request for
+ each key hit. */
+ if (!(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
+ {
+ /* Card present and active, time extension requested. */
+ DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
+ buffer[7], buffer[8]);
+
+ bwi = 1;
+ if (buffer[8] != 0 && buffer[8] != 0xff)
+ bwi = buffer[8];
+
+ /* Gnuk enhancement to prompt user input by ack button */
+ if (buffer[8] == 0xff && !notified)
+ {
+ notified = 1;
+ handle->prompt_cb (handle->prompt_cb_arg, 1);
+ }
+
+ goto retry;
+ }
+
+ if (notified)
+ handle->prompt_cb (handle->prompt_cb_arg, 0);
+
+ if (buffer[0] != expected_type && buffer[0] != RDR_to_PC_SlotStatus)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+ abort_cmd (handle, seqno, 0);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ if (debug_level && (!no_debug || debug_level >= 3))
+ {
+ switch (buffer[0])
+ {
+ case RDR_to_PC_DataBlock:
+ print_r2p_datablock (buffer, msglen);
+ break;
+ case RDR_to_PC_SlotStatus:
+ print_r2p_slotstatus (buffer, msglen);
+ break;
+ case RDR_to_PC_Parameters:
+ print_r2p_parameters (buffer, msglen);
+ break;
+ case RDR_to_PC_Escape:
+ print_r2p_escape (buffer, msglen);
+ break;
+ case RDR_to_PC_DataRate:
+ print_r2p_datarate (buffer, msglen);
+ break;
+ default:
+ print_r2p_unknown (buffer, msglen);
+ break;
+ }
+ }
+ if (CCID_COMMAND_FAILED (buffer))
+ print_command_failed (buffer);
+
+ /* Check whether a card is at all available. Note: If you add new
+ error codes here, check whether they need to be ignored in
+ send_escape_cmd. */
+ switch ((buffer[7] & 0x03))
+ {
+ case 0: /* no error */ break;
+ case 1: rc = CCID_DRIVER_ERR_CARD_INACTIVE; break;
+ case 2: rc = CCID_DRIVER_ERR_NO_CARD; break;
+ case 3: /* RFU */ break;
+ }
+
+ if (rc)
+ {
+ /*
+ * Communication failure by device side.
+ * Possibly, it was forcibly suspended and resumed.
+ */
+ if (handle->ep_intr < 0)
+ {
+ DEBUGOUT ("CCID: card inactive/removed\n");
+ handle->powered_off = 1;
+ }
+
+#if defined(GNUPG_MAJOR_VERSION)
+ scd_kick_the_loop ();
+#endif
+ }
+
+ return rc;
+}
+
+
+
+/* Send an abort sequence and wait until everything settled. */
+static int
+abort_cmd (ccid_driver_t handle, int seqno, int init)
+{
+ int rc;
+ unsigned char dummybuf[8];
+ unsigned char msg[100];
+ int msglen;
+
+ seqno &= 0xff;
+ DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
+ /* Send the abort command to the control pipe. Note that we don't
+ need to keep track of sent abort commands because there should
+ never be another thread using the same slot concurrently. */
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_control_transfer (handle->idev,
+ 0x21,/* bmRequestType: host-to-device,
+ class specific, to interface. */
+ 1, /* ABORT */
+ (seqno << 8 | 0 /* slot */),
+ handle->ifc_no,
+ dummybuf, 0,
+ 1000 /* ms timeout */);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_control_msg error: %s\n", libusb_error_name (rc));
+ if (!init)
+ return map_libusb_error (rc);
+ }
+
+ /* Now send the abort command to the bulk out pipe using the same
+ SEQNO and SLOT. Do this in a loop to so that all seqno are
+ tried. */
+ seqno--; /* Adjust for next increment. */
+ do
+ {
+ int transferred;
+
+ seqno++;
+ msg[0] = PC_to_RDR_Abort;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msglen = 10;
+ set_msg_len (msg, 0);
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out,
+ msg, msglen, &transferred,
+ init? 100: 5000 /* ms timeout */);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc == 0 && transferred == msglen)
+ rc = 0;
+ else if (rc)
+ DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n",
+ libusb_error_name (rc));
+
+ if (rc)
+ return map_libusb_error (rc);
+
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in,
+ msg, sizeof msg, &msglen,
+ init? 100: 5000 /*ms timeout*/);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
+ libusb_error_name (rc));
+ if (init && rc == LIBUSB_ERROR_TIMEOUT)
+ continue;
+ else
+ return map_libusb_error (rc);
+ }
+
+ if (msglen < 10)
+ {
+ DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
+ (unsigned int)msglen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (msg[5] != 0)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n",
+ msg[7], msg[8], msg[9]);
+ if (CCID_COMMAND_FAILED (msg))
+ print_command_failed (msg);
+ }
+ while (rc == LIBUSB_ERROR_TIMEOUT
+ || (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno));
+
+ handle->seqno = ((seqno + 1) & 0xff);
+ DEBUGOUT ("sending abort sequence succeeded\n");
+
+ return 0;
+}
+
+
+/* Note that this function won't return the error codes NO_CARD or
+ CARD_INACTIVE. IF RESULT is not NULL, the result from the
+ operation will get returned in RESULT and its length in RESULTLEN.
+ If the response is larger than RESULTMAX, an error is returned and
+ the required buffer length returned in RESULTLEN. */
+static int
+send_escape_cmd (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *result, size_t resultmax, size_t *resultlen)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+
+ if (resultlen)
+ *resultlen = 0;
+
+ if (datalen > sizeof msg - 10)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */
+
+ msg[0] = PC_to_RDR_Escape;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ memcpy (msg+10, data, datalen);
+ msglen = 10 + datalen;
+ set_msg_len (msg, datalen);
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape,
+ seqno, 5000, 0);
+ if (result)
+ switch (rc)
+ {
+ /* We need to ignore certain errorcode here. */
+ case 0:
+ case CCID_DRIVER_ERR_CARD_INACTIVE:
+ case CCID_DRIVER_ERR_NO_CARD:
+ {
+ if (msglen > resultmax)
+ rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */
+ else
+ {
+ memcpy (result, msg, msglen);
+ if (resultlen)
+ *resultlen = msglen;
+ rc = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+
+int
+ccid_transceive_escape (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp);
+}
+
+
+
+/* experimental */
+int
+ccid_poll (ccid_driver_t handle)
+{
+ int rc;
+ unsigned char msg[10];
+ int msglen;
+ int i, j;
+
+ rc = libusb_interrupt_transfer (handle->idev, handle->ep_intr,
+ msg, sizeof msg, &msglen,
+ 0 /* ms timeout */ );
+ if (rc == LIBUSB_ERROR_TIMEOUT)
+ return 0;
+
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_intr_read error: %s\n", libusb_error_name (rc));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ if (msglen < 1)
+ {
+ DEBUGOUT ("intr-in msg too short\n");
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ if (msg[0] == RDR_to_PC_NotifySlotChange)
+ {
+ DEBUGOUT ("notify slot change:");
+ for (i=1; i < msglen; i++)
+ for (j=0; j < 4; j++)
+ DEBUGOUT_CONT_3 (" %d:%c%c",
+ (i-1)*4+j,
+ (msg[i] & (1<<(j*2)))? 'p':'-',
+ (msg[i] & (2<<(j*2)))? '*':' ');
+ DEBUGOUT_LF ();
+ }
+ else if (msg[0] == RDR_to_PC_HardwareError)
+ {
+ DEBUGOUT ("hardware error occurred\n");
+ }
+ else
+ {
+ DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
+ }
+
+ return 0;
+}
+
+
+/* Note that this function won't return the error codes NO_CARD or
+ CARD_INACTIVE */
+int
+ccid_slot_status (ccid_driver_t handle, int *statusbits, int on_wire)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+ int retries = 0;
+
+ if (handle->powered_off)
+ return CCID_DRIVER_ERR_NO_READER;
+
+ /* If the card (with its lower-level driver) doesn't require
+ GET_STATUS on wire (because it supports INTERRUPT transfer for
+ status change, or it's a token which has a card always inserted),
+ no need to send on wire. */
+ if (!on_wire && !ccid_require_get_status (handle))
+ {
+ /* Setup interrupt transfer at the initial call of slot_status
+ with ON_WIRE == 0 */
+ if (handle->transfer == NULL)
+ ccid_setup_intr (handle);
+
+ *statusbits = 0;
+ return 0;
+ }
+
+ retry:
+ msg[0] = PC_to_RDR_GetSlotStatus;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+
+ rc = bulk_out (handle, msg, 10, 1);
+ if (rc)
+ return rc;
+ /* Note that we set the NO_DEBUG flag here, so that the logs won't
+ get cluttered up by a ticker function checking for the slot
+ status and debugging enabled. */
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
+ seqno, retries? 1000 : 200, 1);
+ if ((rc == CCID_DRIVER_ERR_CARD_IO_ERROR || rc == CCID_DRIVER_ERR_USB_TIMEOUT)
+ && retries < 3)
+ {
+ if (!retries)
+ {
+ DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n");
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_clear_halt (handle->idev, handle->ep_bulk_in);
+ libusb_clear_halt (handle->idev, handle->ep_bulk_out);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ }
+ else
+ DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n");
+ retries++;
+ goto retry;
+ }
+ if (rc && rc != CCID_DRIVER_ERR_NO_CARD && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
+ return rc;
+ *statusbits = (msg[7] & 3);
+
+ return 0;
+}
+
+
+/* Parse ATR string (of ATRLEN) and update parameters at PARAM.
+ Calling this routine, it should prepare default values at PARAM
+ beforehand. This routine assumes that card is accessed by T=1
+ protocol. It doesn't analyze historical bytes at all.
+
+ Returns < 0 value on error:
+ -1 for parse error or integrity check error
+ -2 for card doesn't support T=1 protocol
+ -3 for parameters are nod explicitly defined by ATR
+ -4 for this driver doesn't support CRC
+
+ Returns >= 0 on success:
+ 0 for card is negotiable mode
+ 1 for card is specific mode (and not negotiable)
+ */
+static int
+update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen)
+{
+ int i = -1;
+ int t, y, chk;
+ int historical_bytes_num, negotiable = 1;
+
+#define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0)
+
+ NEXTBYTE ();
+
+ if (atr[i] == 0x3F)
+ param[1] |= 0x02; /* Convention is inverse. */
+ NEXTBYTE ();
+
+ y = (atr[i] >> 4);
+ historical_bytes_num = atr[i] & 0x0f;
+ NEXTBYTE ();
+
+ if ((y & 1))
+ {
+ param[0] = atr[i]; /* TA1 - Fi & Di */
+ NEXTBYTE ();
+ }
+
+ if ((y & 2))
+ NEXTBYTE (); /* TB1 - ignore */
+
+ if ((y & 4))
+ {
+ param[2] = atr[i]; /* TC1 - Guard Time */
+ NEXTBYTE ();
+ }
+
+ if ((y & 8))
+ {
+ y = (atr[i] >> 4); /* TD1 */
+ t = atr[i] & 0x0f;
+ NEXTBYTE ();
+
+ if ((y & 1))
+ { /* TA2 - PPS mode */
+ if ((atr[i] & 0x0f) != 1)
+ return -2; /* Wrong card protocol (!= 1). */
+
+ if ((atr[i] & 0x10) != 0x10)
+ return -3; /* Transmission parameters are implicitly defined. */
+
+ negotiable = 0; /* TA2 means specific mode. */
+ NEXTBYTE ();
+ }
+
+ if ((y & 2))
+ NEXTBYTE (); /* TB2 - ignore */
+
+ if ((y & 4))
+ NEXTBYTE (); /* TC2 - ignore */
+
+ if ((y & 8))
+ {
+ y = (atr[i] >> 4); /* TD2 */
+ t = atr[i] & 0x0f;
+ NEXTBYTE ();
+ }
+ else
+ y = 0;
+
+ while (y)
+ {
+ if ((y & 1))
+ { /* TAx */
+ if (t == 1)
+ param[5] = atr[i]; /* IFSC */
+ else if (t == 15)
+ /* XXX: check voltage? */
+ param[4] = (atr[i] >> 6); /* ClockStop */
+
+ NEXTBYTE ();
+ }
+
+ if ((y & 2))
+ {
+ if (t == 1)
+ param[3] = atr[i]; /* TBx - BWI & CWI */
+ NEXTBYTE ();
+ }
+
+ if ((y & 4))
+ {
+ if (t == 1)
+ param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */
+ NEXTBYTE ();
+
+ if (param[1] & 0x01)
+ return -4; /* CRC not supported yet. */
+ }
+
+ if ((y & 8))
+ {
+ y = (atr[i] >> 4); /* TDx */
+ t = atr[i] & 0x0f;
+ NEXTBYTE ();
+ }
+ else
+ y = 0;
+ }
+ }
+
+ i += historical_bytes_num - 1;
+ NEXTBYTE ();
+ if (atrlen != i+1)
+ return -1;
+
+#undef NEXTBYTE
+
+ chk = 0;
+ do
+ {
+ chk ^= atr[i];
+ i--;
+ }
+ while (i > 0);
+
+ if (chk != 0)
+ return -1;
+
+ return negotiable;
+}
+
+
+/* Return the ATR of the card. This is not a cached value and thus an
+ actual reset is done. */
+int
+ccid_get_atr (ccid_driver_t handle,
+ unsigned char *atr, size_t maxatrlen, size_t *atrlen)
+{
+ int rc;
+ int statusbits;
+ unsigned char msg[100];
+ unsigned char *tpdu;
+ size_t msglen, tpdulen;
+ unsigned char seqno;
+ int use_crc = 0;
+ unsigned int edc;
+ int tried_iso = 0;
+ int got_param;
+ unsigned char param[7] = { /* For Protocol T=1 */
+ 0x11, /* bmFindexDindex */
+ 0x10, /* bmTCCKST1 */
+ 0x00, /* bGuardTimeT1 */
+ 0x4d, /* bmWaitingIntegersT1 */
+ 0x00, /* bClockStop */
+ 0x20, /* bIFSC */
+ 0x00 /* bNadValue */
+ };
+
+ /* First check whether a card is available. */
+ rc = ccid_slot_status (handle, &statusbits, 1);
+ if (rc)
+ return rc;
+ if (statusbits == 2)
+ return CCID_DRIVER_ERR_NO_CARD;
+
+ /*
+ * In the first invocation of ccid_slot_status, card reader may
+ * return CCID_DRIVER_ERR_CARD_INACTIVE and handle->powered_off may
+ * become 1. Because inactive card is no problem (we are turning it
+ * ON here), clear the flag.
+ */
+ handle->powered_off = 0;
+
+ /* For an inactive and also for an active card, issue the PowerOn
+ command to get the ATR. */
+ again:
+ msg[0] = PC_to_RDR_IccPowerOn;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+ msg[7] = handle->auto_voltage ? 0 : 1;
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
+ seqno, 5000, 0);
+ if (rc)
+ return rc;
+ if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb
+ && ((handle->id_vendor == VENDOR_CHERRY
+ && handle->id_product == 0x0005)
+ || (handle->id_vendor == VENDOR_GEMPC
+ && handle->id_product == 0x4433)
+ ))
+ {
+ tried_iso = 1;
+ /* Try switching to ISO mode. */
+ if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2,
+ NULL, 0, NULL))
+ goto again;
+ }
+ else if (statusbits == 0 && CCID_COMMAND_FAILED (msg))
+ {
+ /* Card was active already, and something went wrong with
+ PC_to_RDR_IccPowerOn command. It may be baud-rate mismatch
+ between the card and the reader. To recover from this state,
+ send PC_to_RDR_IccPowerOff command to reset the card and try
+ again.
+ */
+ rc = send_power_off (handle);
+ if (rc)
+ return rc;
+
+ statusbits = 1;
+ goto again;
+ }
+ else if (CCID_COMMAND_FAILED (msg))
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+
+ handle->powered_off = 0;
+
+ if (atr)
+ {
+ size_t n = msglen - 10;
+
+ if (n > maxatrlen)
+ n = maxatrlen;
+ memcpy (atr, msg+10, n);
+ *atrlen = n;
+ }
+
+ param[6] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ rc = update_param_by_atr (param, msg+10, msglen - 10);
+ if (rc < 0)
+ {
+ DEBUGOUT_1 ("update_param_by_atr failed: %d\n", rc);
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ got_param = 0;
+
+ if (handle->auto_param)
+ {
+ msg[0] = PC_to_RDR_GetParameters;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (!rc)
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+ seqno, 2000, 0);
+ if (rc)
+ DEBUGOUT ("GetParameters failed\n");
+ else if (msglen == 17 && msg[9] == 1)
+ got_param = 1;
+ }
+ else if (handle->auto_pps)
+ ;
+ else if (rc == 1) /* It's negotiable, send PPS. */
+ {
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0;
+ msg[8] = 0;
+ msg[9] = 0;
+ msg[10] = 0xff; /* PPSS */
+ msg[11] = 0x11; /* PPS0: PPS1, Protocol T=1 */
+ msg[12] = param[0]; /* PPS1: Fi / Di */
+ msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */
+ set_msg_len (msg, 4);
+ msglen = 10 + 4;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
+ seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ if (msglen != 10 + 4)
+ {
+ DEBUGOUT_1 ("Setting PPS failed: %zu\n", msglen);
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0])
+ {
+ DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]);
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+
+ /* Setup parameters to select T=1. */
+ msg[0] = PC_to_RDR_SetParameters;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 1; /* Select T=1. */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+
+ if (!got_param)
+ memcpy (&msg[10], param, 7);
+ set_msg_len (msg, 7);
+ msglen = 10 + 7;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+ seqno, 5000, 0);
+ if (rc)
+ DEBUGOUT ("SetParameters failed (ignored)\n");
+
+ if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
+ handle->ifsc = msg[15];
+ else
+ handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */
+
+ if (handle->nonnull_nad && msglen > 16 && msg[16] == 0)
+ {
+ DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n");
+ handle->nonnull_nad = 0;
+ }
+
+ handle->t1_ns = 0;
+ handle->t1_nr = 0;
+
+ /* Send an S-Block with our maximum IFSD to the CCID. */
+ if (!handle->apdu_level && !handle->auto_ifsd)
+ {
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
+ tpdu[2] = 1;
+ tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0;
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, tpdulen);
+ msglen = 10 + tpdulen;
+
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ return CCID_DRIVER_ERR_ABORTED;
+
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
+ {
+ DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
+ return -1;
+ }
+ DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
+ }
+
+ ccid_vendor_specific_setup (handle);
+ return 0;
+}
+
+
+
+
+static unsigned int
+compute_edc (const unsigned char *data, size_t datalen, int use_crc)
+{
+ if (use_crc)
+ {
+ return 0x42; /* Not yet implemented. */
+ }
+ else
+ {
+ unsigned char crc = 0;
+
+ for (; datalen; datalen--)
+ crc ^= *data++;
+ return crc;
+ }
+}
+
+
+/* Return true if APDU is an extended length one. */
+static int
+is_exlen_apdu (const unsigned char *apdu, size_t apdulen)
+{
+ if (apdulen < 7 || apdu[4])
+ return 0; /* Too short or no Z byte. */
+ return 1;
+}
+
+
+/* Helper for ccid_transceive used for APDU level exchanges. */
+static int
+ccid_transceive_apdu_level (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_len,
+ unsigned char *resp, size_t maxresplen,
+ size_t *nresp)
+{
+ int rc;
+ unsigned char msg[CCID_MAX_BUF];
+ const unsigned char *apdu_p;
+ size_t apdu_part_len;
+ size_t msglen;
+ unsigned char seqno;
+ int bwi = 0;
+ unsigned char chain = 0;
+
+ if (apdu_len == 0 || apdu_len > sizeof (msg) - 10)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
+
+ apdu_p = apdu_buf;
+ while (1)
+ {
+ apdu_part_len = apdu_len;
+ if (apdu_part_len > handle->max_ccid_msglen - 10)
+ {
+ apdu_part_len = handle->max_ccid_msglen - 10;
+ chain |= 0x01;
+ }
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi;
+ msg[8] = chain;
+ msg[9] = 0;
+ memcpy (msg+10, apdu_p, apdu_part_len);
+ set_msg_len (msg, apdu_part_len);
+ msglen = 10 + apdu_part_len;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ apdu_p += apdu_part_len;
+ apdu_len -= apdu_part_len;
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, CCID_CMD_TIMEOUT, 0);
+ if (rc)
+ return rc;
+
+ if (!(chain & 0x01))
+ break;
+
+ chain = 0x02;
+ }
+
+ apdu_len = 0;
+ while (1)
+ {
+ apdu_part_len = msglen - 10;
+ if (resp && apdu_len + apdu_part_len <= maxresplen)
+ memcpy (resp + apdu_len, msg+10, apdu_part_len);
+ apdu_len += apdu_part_len;
+
+ if (!(msg[9] & 0x01))
+ break;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi;
+ msg[8] = 0x10; /* Request next data block */
+ msg[9] = 0;
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, CCID_CMD_TIMEOUT, 0);
+ if (rc)
+ return rc;
+ }
+
+ if (resp)
+ {
+ if (apdu_len > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)apdu_len, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ *nresp = apdu_len;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ Protocol T=1 overview
+
+ Block Structure:
+ Prologue Field:
+ 1 byte Node Address (NAD)
+ 1 byte Protocol Control Byte (PCB)
+ 1 byte Length (LEN)
+ Information Field:
+ 0-254 byte APDU or Control Information (INF)
+ Epilogue Field:
+ 1 byte Error Detection Code (EDC)
+
+ NAD:
+ bit 7 unused
+ bit 4..6 Destination Node Address (DAD)
+ bit 3 unused
+ bit 2..0 Source Node Address (SAD)
+
+ If node adresses are not used, SAD and DAD should be set to 0 on
+ the first block sent to the card. If they are used they should
+ have different values (0 for one is okay); that first block sets up
+ the addresses of the nodes.
+
+ PCB:
+ Information Block (I-Block):
+ bit 7 0
+ bit 6 Sequence number (yep, that is modulo 2)
+ bit 5 Chaining flag
+ bit 4..0 reserved
+ Received-Ready Block (R-Block):
+ bit 7 1
+ bit 6 0
+ bit 5 0
+ bit 4 Sequence number
+ bit 3..0 0 = no error
+ 1 = EDC or parity error
+ 2 = other error
+ other values are reserved
+ Supervisory Block (S-Block):
+ bit 7 1
+ bit 6 1
+ bit 5 clear=request,set=response
+ bit 4..0 0 = resynchronization request
+ 1 = information field size request
+ 2 = abort request
+ 3 = extension of BWT request
+ 4 = VPP error
+ other values are reserved
+
+*/
+
+int
+ccid_transceive (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_buflen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ int rc;
+ /* The size of the buffer used to be 10+259. For the via_escape
+ hack we need one extra byte, thus 11+259. */
+ unsigned char send_buffer[11+259], recv_buffer[11+259];
+ const unsigned char *apdu;
+ size_t apdulen;
+ unsigned char *msg, *tpdu, *p;
+ size_t msglen, tpdulen, last_tpdulen, n;
+ unsigned char seqno;
+ unsigned int edc;
+ int use_crc = 0;
+ int hdrlen, pcboff;
+ size_t dummy_nresp;
+ int via_escape = 0;
+ int next_chunk = 1;
+ int sending = 1;
+ int retries = 0;
+ int resyncing = 0;
+ int nad_byte;
+ int wait_more = 0;
+
+ if (!nresp)
+ nresp = &dummy_nresp;
+ *nresp = 0;
+
+ /* Smarter readers allow sending APDUs directly; divert here. */
+ if (handle->apdu_level)
+ {
+ /* We employ a hack for Omnikey readers which are able to send
+ TPDUs using an escape sequence. There is no documentation
+ but the Windows driver does it this way. Tested using a
+ CM6121. This method works also for the Cherry XX44
+ keyboards; however there are problems with the
+ ccid_transceive_secure which leads to a loss of sync on the
+ CCID level. If Cherry wants to make their keyboard work
+ again, they should hand over some docs. */
+ if ((handle->id_vendor == VENDOR_OMNIKEY)
+ && handle->apdu_level < 2
+ && is_exlen_apdu (apdu_buf, apdu_buflen))
+ via_escape = 1;
+ else
+ return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
+ resp, maxresplen, nresp);
+ }
+
+ /* The other readers we support require sending TPDUs. */
+
+ tpdulen = 0; /* Avoid compiler warning about no initialization. */
+ msg = send_buffer;
+ hdrlen = via_escape? 11 : 10;
+
+ /* NAD: DAD=1, SAD=0 */
+ nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ if (via_escape)
+ nad_byte = 0;
+
+ last_tpdulen = 0; /* Avoid gcc warning (controlled by RESYNCING). */
+ for (;;)
+ {
+ if (next_chunk)
+ {
+ next_chunk = 0;
+
+ apdu = apdu_buf;
+ apdulen = apdu_buflen;
+ assert (apdulen);
+
+ /* Construct an I-Block. */
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
+ if (apdulen > handle->ifsc )
+ {
+ apdulen = handle->ifsc;
+ apdu_buf += handle->ifsc;
+ apdu_buflen -= handle->ifsc;
+ tpdu[1] |= (1 << 5); /* Set more bit. */
+ }
+ tpdu[2] = apdulen;
+ memcpy (tpdu+3, apdu, apdulen);
+ tpdulen = 3 + apdulen;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ }
+
+ if (via_escape)
+ {
+ msg[0] = PC_to_RDR_Escape;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msg[10] = 0x1a; /* Omnikey command to send a TPDU. */
+ set_msg_len (msg, 1 + tpdulen);
+ }
+ else
+ {
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = (wait_more ? wait_more : 1); /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, tpdulen);
+ }
+ msglen = hdrlen + tpdulen;
+ if (!resyncing)
+ last_tpdulen = tpdulen;
+ pcboff = hdrlen+1;
+
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+ (msg[pcboff] & 0x80)? 'S' : 'I',
+ ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+ : !!(msg[pcboff] & 0x40)),
+ (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+ " [more]":""));
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno,
+ (wait_more ? wait_more : 1) * CCID_CMD_TIMEOUT, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + hdrlen;
+ tpdulen = msglen - hdrlen;
+ resyncing = 0;
+
+ if (tpdulen < 4)
+ {
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_clear_halt (handle->idev, handle->ep_bulk_in);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ return CCID_DRIVER_ERR_ABORTED;
+ }
+
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[pcboff] & 0xc0) == 0x80)? 'R' :
+ (msg[pcboff] & 0x80)? 'S' : 'I',
+ ((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
+ : !!(msg[pcboff] & 0x40)),
+ ((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0,
+ (!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
+ " [more]":""));
+
+ wait_more = 0;
+ if (!(tpdu[1] & 0x80))
+ { /* This is an I-block. */
+ retries = 0;
+ if (sending)
+ { /* last block sent was successful. */
+ handle->t1_ns ^= 1;
+ sending = 0;
+ }
+
+ if (!!(tpdu[1] & 0x40) != handle->t1_nr)
+ { /* Response does not match our sequence number. */
+ msg = send_buffer;
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+
+ continue;
+ }
+
+ handle->t1_nr ^= 1;
+
+ p = tpdu + 3; /* Skip the prologue field. */
+ n = tpdulen - 3 - 1; /* Strip the epilogue field. */
+ /* fixme: verify the checksum. */
+ if (resp)
+ {
+ if (n > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, p, n);
+ resp += n;
+ *nresp += n;
+ maxresplen -= n;
+ }
+
+ if (!(tpdu[1] & 0x20))
+ return 0; /* No chaining requested - ready. */
+
+ msg = send_buffer;
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ }
+ else if ((tpdu[1] & 0xc0) == 0x80)
+ { /* This is a R-block. */
+ if ( (tpdu[1] & 0x0f))
+ {
+ retries++;
+ if (via_escape && retries == 1 && (msg[pcboff] & 0x0f))
+ {
+ /* Error probably due to switching to TPDU. Send a
+ resync request. We use the recv_buffer so that
+ we don't corrupt the send_buffer. */
+ msg = recv_buffer;
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = 0xc0; /* S-block resync request. */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ resyncing = 1;
+ DEBUGOUT ("T=1: requesting resync\n");
+ }
+ else if (retries > 3)
+ {
+ DEBUGOUT ("T=1: 3 failed retries\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else
+ {
+ /* Error: repeat last block */
+ msg = send_buffer;
+ tpdulen = last_tpdulen;
+ }
+ }
+ else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
+ { /* Response does not match our sequence number. */
+ DEBUGOUT ("R-block with wrong seqno received on more bit\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if (sending)
+ { /* Send next chunk. */
+ retries = 0;
+ msg = send_buffer;
+ next_chunk = 1;
+ handle->t1_ns ^= 1;
+ }
+ else
+ {
+ DEBUGOUT ("unexpected ACK R-block received\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+ else
+ { /* This is a S-block. */
+ retries = 0;
+ DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n",
+ (tpdu[1] & 0x20)? "response": "request",
+ (tpdu[1] & 0x1f));
+ if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
+ {
+ /* Information field size request. */
+ unsigned char ifsc = tpdu[3];
+
+ if (ifsc < 16 || ifsc > 254)
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+ msg = send_buffer;
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
+ tpdu[2] = 1;
+ tpdu[3] = ifsc;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc);
+ }
+ else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+ {
+ /* Wait time extension request. */
+ unsigned char bwi = tpdu[3];
+
+ wait_more = bwi;
+
+ msg = send_buffer;
+ tpdu = msg + hdrlen;
+ tpdu[0] = nad_byte;
+ tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
+ tpdu[2] = 1;
+ tpdu[3] = bwi;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi);
+ print_progress (handle);
+ }
+ else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2])
+ {
+ DEBUGOUT ("T=1: resync ack from reader\n");
+ /* Repeat previous block. */
+ msg = send_buffer;
+ tpdulen = last_tpdulen;
+ }
+ else
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ } /* end T=1 protocol loop. */
+
+ return 0;
+}
+
+
+/* Send the CCID Secure command to the reader. APDU_BUF should
+ contain the APDU template. PIN_MODE defines how the pin gets
+ formatted:
+
+ 1 := The PIN is ASCII encoded and of variable length. The
+ length of the PIN entered will be put into Lc by the reader.
+ The APDU should me made up of 4 bytes without Lc.
+
+ PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0
+ may be used t enable reasonable defaults.
+
+ When called with RESP and NRESP set to NULL, the function will
+ merely check whether the reader supports the secure command for the
+ given APDU and PIN_MODE. */
+int
+ccid_transceive_secure (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_buflen,
+ pininfo_t *pininfo,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ int rc;
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
+ unsigned char *msg, *tpdu, *p;
+ size_t msglen, tpdulen, n;
+ unsigned char seqno;
+ size_t dummy_nresp;
+ int testmode;
+ int cherry_mode = 0;
+ int add_zero = 0;
+ int enable_varlen = 0;
+
+ testmode = !resp && !nresp;
+
+ if (!nresp)
+ nresp = &dummy_nresp;
+ *nresp = 0;
+
+ if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1))
+ ;
+ else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2))
+ ;
+ else
+ return CCID_DRIVER_ERR_NO_PINPAD;
+
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 15;
+
+ /* Note that the 25 is the maximum value the SPR532 allows. */
+ if (pininfo->minlen < 1 || pininfo->minlen > 25
+ || pininfo->maxlen < 1 || pininfo->maxlen > 25
+ || pininfo->minlen > pininfo->maxlen)
+ return CCID_DRIVER_ERR_INV_VALUE;
+
+ /* We have only tested a few readers so better don't risk anything
+ and do not allow the use with other readers. */
+ switch (handle->id_vendor)
+ {
+ case VENDOR_SCM: /* Tested with SPR 532. */
+ case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
+ case VENDOR_FSIJ: /* Tested with Gnuk (0.21). */
+ pininfo->maxlen = 25;
+ enable_varlen = 1;
+ break;
+ case VENDOR_REINER:/* Tested with cyberJack go */
+ case VENDOR_VASCO: /* Tested with DIGIPASS 920 */
+ enable_varlen = 1;
+ break;
+ case VENDOR_CHERRY:
+ pininfo->maxlen = 15;
+ enable_varlen = 1;
+ /* The CHERRY XX44 keyboard echos an asterisk for each entered
+ character on the keyboard channel. We use a special variant
+ of PC_to_RDR_Secure which directs these characters to the
+ smart card's bulk-in channel. We also need to append a zero
+ Lc byte to the APDU. It seems that it will be replaced with
+ the actual length instead of being appended before the APDU
+ is send to the card. */
+ add_zero = 1;
+ if (handle->id_product != CHERRY_ST2000)
+ cherry_mode = 1;
+ break;
+ case VENDOR_NXP:
+ if (handle->id_product == CRYPTOUCAN){
+ pininfo->maxlen = 25;
+ enable_varlen = 1;
+ break;
+ }
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+ case VENDOR_GEMPC:
+ if (handle->id_product == GEMPC_PINPAD)
+ {
+ enable_varlen = 0;
+ pininfo->minlen = 4;
+ pininfo->maxlen = 8;
+ break;
+ }
+ else if (handle->id_product == GEMPC_EZIO)
+ {
+ pininfo->maxlen = 25;
+ enable_varlen = 1;
+ break;
+ }
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+ default:
+ if ((handle->id_vendor == VENDOR_VEGA &&
+ handle->id_product == VEGA_ALPHA))
+ {
+ enable_varlen = 0;
+ pininfo->minlen = 4;
+ pininfo->maxlen = 8;
+ break;
+ }
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+ }
+
+ if (enable_varlen)
+ pininfo->fixedlen = 0;
+
+ if (testmode)
+ return 0; /* Success */
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+
+ ccid_vendor_specific_pinpad_setup (handle);
+
+ msg = send_buffer;
+ msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msg[10] = apdu_buf[1] == 0x20 ? 0 : 1;
+ /* Perform PIN verification or PIN modification. */
+ msg[11] = 0; /* Timeout in seconds. */
+ msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ if (handle->id_vendor == VENDOR_SCM)
+ {
+ /* For the SPR532 the next 2 bytes need to be zero. We do this
+ for all SCM products. Kudos to Martin Paljak for this
+ hint. */
+ msg[13] = msg[14] = 0;
+ }
+ else
+ {
+ msg[13] = pininfo->fixedlen; /* bmPINBlockString:
+ 0 bits of pin length to insert.
+ PIN block size by fixedlen. */
+ msg[14] = 0x00; /* bmPINLengthFormat:
+ Units are bytes, position is 0. */
+ }
+
+ msglen = 15;
+ if (apdu_buf[1] == 0x24)
+ {
+ msg[msglen++] = 0; /* bInsertionOffsetOld */
+ msg[msglen++] = pininfo->fixedlen; /* bInsertionOffsetNew */
+ }
+
+ /* The following is a little endian word. */
+ msg[msglen++] = pininfo->maxlen; /* wPINMaxExtraDigit-Maximum. */
+ msg[msglen++] = pininfo->minlen; /* wPINMaxExtraDigit-Minimum. */
+
+ if (apdu_buf[1] == 0x24)
+ msg[msglen++] = apdu_buf[2] == 0 ? 0x03 : 0x01;
+ /* bConfirmPIN
+ * 0x00: new PIN once
+ * 0x01: new PIN twice (confirmation)
+ * 0x02: old PIN and new PIN once
+ * 0x03: old PIN and new PIN twice (confirmation)
+ */
+
+ msg[msglen] = 0x02; /* bEntryValidationCondition:
+ Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ msg[msglen] |= 0x01; /* Max size reached. */
+ msglen++;
+
+ if (apdu_buf[1] == 0x20)
+ msg[msglen++] = 0x01; /* bNumberMessage. */
+ else
+ msg[msglen++] = 0x03; /* bNumberMessage. */
+
+ msg[msglen++] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
+ msg[msglen++] = 0x04; /* wLangId-High. */
+
+ if (apdu_buf[1] == 0x20)
+ msg[msglen++] = 0; /* bMsgIndex. */
+ else
+ {
+ msg[msglen++] = 0; /* bMsgIndex1. */
+ msg[msglen++] = 1; /* bMsgIndex2. */
+ msg[msglen++] = 2; /* bMsgIndex3. */
+ }
+
+ /* Calculate Lc. */
+ n = pininfo->fixedlen;
+ if (apdu_buf[1] == 0x24)
+ n += pininfo->fixedlen;
+
+ /* bTeoProlog follows: */
+ msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */
+ if (n)
+ msg[msglen++] = n + 5; /* apdulen should be filled for fixed length. */
+ else
+ msg[msglen++] = 0; /* The apdulen will be filled in by the reader. */
+ /* APDU follows: */
+ msg[msglen++] = apdu_buf[0]; /* CLA */
+ msg[msglen++] = apdu_buf[1]; /* INS */
+ msg[msglen++] = apdu_buf[2]; /* P1 */
+ msg[msglen++] = apdu_buf[3]; /* P2 */
+ if (add_zero)
+ msg[msglen++] = 0;
+ else if (pininfo->fixedlen != 0)
+ {
+ msg[msglen++] = n;
+ memset (&msg[msglen], 0xff, n);
+ msglen += n;
+ }
+ /* An EDC is not required. */
+ set_msg_len (msg, msglen - 10);
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ RDR_to_PC_DataBlock, seqno, 30000, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (handle->apdu_level)
+ {
+ if (resp)
+ {
+ if (tpdulen > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)tpdulen, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, tpdu, tpdulen);
+ *nresp = tpdulen;
+ }
+ return 0;
+ }
+
+ if (tpdulen < 4)
+ {
+#ifdef USE_NPTH
+ npth_unprotect ();
+#endif
+ libusb_clear_halt (handle->idev, handle->ep_bulk_in);
+#ifdef USE_NPTH
+ npth_protect ();
+#endif
+ return CCID_DRIVER_ERR_ABORTED;
+ }
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ if (!(tpdu[1] & 0x80))
+ { /* This is an I-block. */
+ /* Last block sent was successful. */
+ handle->t1_ns ^= 1;
+
+ if (!!(tpdu[1] & 0x40) != handle->t1_nr)
+ { /* Response does not match our sequence number. */
+ DEBUGOUT ("I-block with wrong seqno received\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ handle->t1_nr ^= 1;
+
+ p = tpdu + 3; /* Skip the prologue field. */
+ n = tpdulen - 3 - 1; /* Strip the epilogue field. */
+ /* fixme: verify the checksum. */
+ if (resp)
+ {
+ if (n > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, p, n);
+ *nresp += n;
+ }
+
+ if (!(tpdu[1] & 0x20))
+ return 0; /* No chaining requested - ready. */
+
+ DEBUGOUT ("chaining requested but not supported for Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if ((tpdu[1] & 0xc0) == 0x80)
+ { /* This is a R-block. */
+ if ( (tpdu[1] & 0x0f))
+ { /* Error: repeat last block */
+ DEBUGOUT ("No retries supported for Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if (!!(tpdu[1] & 0x10) == handle->t1_ns)
+ { /* Response does not match our sequence number. */
+ DEBUGOUT ("R-block with wrong seqno received on more bit\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else
+ { /* Send next chunk. */
+ DEBUGOUT ("chaining not supported on Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+ else
+ { /* This is a S-block. */
+ DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n",
+ (tpdu[1] & 0x20)? "response": "request",
+ (tpdu[1] & 0x1f));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ return 0;
+}
+
+
+
+
+#ifdef TEST
+
+
+static void
+print_error (int err)
+{
+ const char *p;
+ char buf[50];
+
+ switch (err)
+ {
+ case 0: p = "success"; break;
+ case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break;
+ case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break;
+ case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break;
+ case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break;
+ case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break;
+ case CCID_DRIVER_ERR_BUSY: p = "busy"; break;
+ case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break;
+ case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break;
+ case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break;
+ case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break;
+ case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break;
+ case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break;
+ default: sprintf (buf, "0x%05x", err); p = buf; break;
+ }
+ fprintf (stderr, "operation failed: %s\n", p);
+}
+
+
+static void
+print_data (const unsigned char *data, size_t length)
+{
+ if (length >= 2)
+ {
+ fprintf (stderr, "operation status: %02X%02X\n",
+ data[length-2], data[length-1]);
+ length -= 2;
+ }
+ if (length)
+ {
+ fputs (" returned data:", stderr);
+ for (; length; length--, data++)
+ fprintf (stderr, " %02X", *data);
+ putc ('\n', stderr);
+ }
+}
+
+static void
+print_result (int rc, const unsigned char *data, size_t length)
+{
+ if (rc)
+ print_error (rc);
+ else if (data)
+ print_data (data, length);
+}
+
+int
+main (int argc, char **argv)
+{
+ gpg_error_t err;
+ ccid_driver_t ccid;
+ int slotstat;
+ unsigned char result[512];
+ size_t resultlen;
+ int no_pinpad = 0;
+ int verify_123456 = 0;
+ int did_verify = 0;
+ int no_poll = 0;
+ int idx_max;
+ struct ccid_dev_table *ccid_table;
+
+ if (argc)
+ {
+ argc--;
+ argv++;
+ }
+
+ while (argc)
+ {
+ if ( !strcmp (*argv, "--list"))
+ {
+ char *p;
+ p = ccid_get_reader_list ();
+ if (!p)
+ return 1;
+ fputs (p, stderr);
+ free (p);
+ return 0;
+ }
+ else if ( !strcmp (*argv, "--debug"))
+ {
+ ccid_set_debug_level (ccid_set_debug_level (-1)+1);
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--no-poll"))
+ {
+ no_poll = 1;
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--no-pinpad"))
+ {
+ no_pinpad = 1;
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--verify-123456"))
+ {
+ verify_123456 = 1;
+ argc--; argv++;
+ }
+ else
+ break;
+ }
+
+ err = ccid_dev_scan (&idx_max, &ccid_table);
+ if (err)
+ return 1;
+
+ if (idx_max == 0)
+ return 1;
+
+ err = ccid_open_reader (argc? *argv:NULL, 0, ccid_table, &ccid, NULL);
+ if (err)
+ return 1;
+
+ ccid_dev_scan_finish (ccid_table, idx_max);
+
+ if (!no_poll)
+ ccid_poll (ccid);
+ fputs ("getting ATR ...\n", stderr);
+ err = ccid_get_atr (ccid, NULL, 0, NULL);
+ if (err)
+ {
+ print_error (err);
+ return 1;
+ }
+
+ if (!no_poll)
+ ccid_poll (ccid);
+ fputs ("getting slot status ...\n", stderr);
+ err = ccid_slot_status (ccid, &slotstat, 1);
+ if (err)
+ {
+ print_error (err);
+ return 1;
+ }
+
+ if (!no_poll)
+ ccid_poll (ccid);
+
+ fputs ("selecting application OpenPGP ....\n", stderr);
+ {
+ static unsigned char apdu[] = {
+ 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
+ err = ccid_transceive (ccid,
+ apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (err, result, resultlen);
+ }
+
+
+ if (!no_poll)
+ ccid_poll (ccid);
+
+ fputs ("getting OpenPGP DO 0x65 ....\n", stderr);
+ {
+ static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 };
+ err = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (err, result, resultlen);
+ }
+
+ if (!no_pinpad)
+ {
+ }
+
+ if (!no_pinpad)
+ {
+ static unsigned char apdu[] = { 0, 0x20, 0, 0x81 };
+ pininfo_t pininfo = { 0, 0, 0 };
+
+ if (ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo,
+ NULL, 0, NULL))
+ fputs ("can't verify using a PIN-Pad reader\n", stderr);
+ else
+ {
+ fputs ("verifying CHV1 using the PINPad ....\n", stderr);
+
+ err = ccid_transceive_secure (ccid, apdu, sizeof apdu, &pininfo,
+ result, sizeof result, &resultlen);
+ print_result (err, result, resultlen);
+ did_verify = 1;
+ }
+ }
+
+ if (verify_123456 && !did_verify)
+ {
+ fputs ("verifying that CHV1 is 123456....\n", stderr);
+ {
+ static unsigned char apdu[] = {0, 0x20, 0, 0x81,
+ 6, '1','2','3','4','5','6'};
+ err = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (err, result, resultlen);
+ }
+ }
+
+ if (!err)
+ {
+ fputs ("getting OpenPGP DO 0x5E ....\n", stderr);
+ {
+ static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 };
+ err = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (err, result, resultlen);
+ }
+ }
+
+ ccid_close_reader (ccid);
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -DTEST -DGPGRT_ENABLE_ES_MACROS -DHAVE_NPTH -DUSE_NPTH -Wall -I/usr/include/libusb-1.0 -I/usr/local/include -lusb-1.0 -g ccid-driver.c -lnpth -lgpg-error"
+ * End:
+ */
+#endif /*TEST*/
+#endif /*HAVE_LIBUSB*/
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
new file mode 100644
index 0000000..dc17c27
--- /dev/null
+++ b/scd/ccid-driver.h
@@ -0,0 +1,158 @@
+/* ccid-driver.h - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ *
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU General Public License. If you wish to
+ * allow use of your version of this file only under the terms of the
+ * GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef CCID_DRIVER_H
+#define CCID_DRIVER_H
+
+
+#ifdef CCID_DRIVER_INCLUDE_USB_IDS
+/* We need to know the vendor to do some hacks. */
+enum {
+ VENDOR_CHERRY = 0x046a,
+ VENDOR_SCM = 0x04e6,
+ VENDOR_OMNIKEY= 0x076b,
+ VENDOR_GEMPC = 0x08e6,
+ VENDOR_VEGA = 0x0982,
+ VENDOR_REINER = 0x0c4b,
+ VENDOR_KAAN = 0x0d46,
+ VENDOR_FSIJ = 0x234b,
+ VENDOR_VASCO = 0x1a44,
+ VENDOR_NXP = 0x1fc9,
+};
+
+
+/* Some product ids. */
+#define SCM_SCR331 0xe001
+#define SCM_SCR331DI 0x5111
+#define SCM_SCR335 0x5115
+#define SCM_SCR3320 0x5117
+#define SCM_SPR532 0xe003 /* Also used succeeding model SPR332. */
+#define CHERRY_ST2000 0x003e
+#define VASCO_920 0x0920
+#define GEMPC_PINPAD 0x3478
+#define GEMPC_CT30 0x3437
+#define GEMPC_EZIO 0x34c2 /* (!=34c0) Also known as IDBridge CT710 */
+#define VEGA_ALPHA 0x0008
+#define CYBERJACK_GO 0x0504
+#define CRYPTOUCAN 0x81e6
+
+#endif /*CCID_DRIVER_INCLUDE_USB_IDS*/
+
+
+/* The CID driver returns the same error codes as the status words
+ used by GnuPG's apdu.h. For ease of maintenance they should always
+ match. */
+#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001
+#define CCID_DRIVER_ERR_INV_VALUE 0x10002
+#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003
+#define CCID_DRIVER_ERR_NO_DRIVER 0x10004
+#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005
+#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006
+#define CCID_DRIVER_ERR_BUSY 0x10007
+#define CCID_DRIVER_ERR_NO_CARD 0x10008
+#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009
+#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a
+#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b
+#define CCID_DRIVER_ERR_NO_READER 0x1000c
+#define CCID_DRIVER_ERR_ABORTED 0x1000d
+#define CCID_DRIVER_ERR_NO_PINPAD 0x1000e
+#define CCID_DRIVER_ERR_USB_OTHER 0x10020
+#define CCID_DRIVER_ERR_USB_IO 0x10021
+#define CCID_DRIVER_ERR_USB_ACCESS 0x10023
+#define CCID_DRIVER_ERR_USB_NO_DEVICE 0x10024
+#define CCID_DRIVER_ERR_USB_BUSY 0x10026
+#define CCID_DRIVER_ERR_USB_TIMEOUT 0x10027
+#define CCID_DRIVER_ERR_USB_OVERFLOW 0x10028
+
+struct ccid_driver_s;
+typedef struct ccid_driver_s *ccid_driver_t;
+
+struct ccid_dev_table;
+
+int ccid_set_debug_level (int level);
+char *ccid_get_reader_list (void);
+
+gpg_error_t ccid_dev_scan (int *idx_max, void **t_p);
+void ccid_dev_scan_finish (void *tbl0, int max);
+unsigned int ccid_get_BAI (int, void *tbl0);
+int ccid_compare_BAI (ccid_driver_t handle, unsigned int);
+int ccid_open_reader (const char *spec_reader_name,
+ int idx, void *ccid_table0,
+ ccid_driver_t *handle, char **rdrname_p);
+int ccid_set_progress_cb (ccid_driver_t handle,
+ void (*cb)(void *, const char *, int, int, int),
+ void *cb_arg);
+int ccid_set_prompt_cb (ccid_driver_t handle, void (*cb)(void *, int),
+ void *cb_arg);
+int ccid_shutdown_reader (ccid_driver_t handle);
+int ccid_close_reader (ccid_driver_t handle);
+int ccid_get_atr (ccid_driver_t handle,
+ unsigned char *atr, size_t maxatrlen, size_t *atrlen);
+int ccid_slot_status (ccid_driver_t handle, int *statusbits, int on_wire);
+int ccid_transceive (ccid_driver_t handle,
+ const unsigned char *apdu, size_t apdulen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp);
+int ccid_transceive_secure (ccid_driver_t handle,
+ const unsigned char *apdu, size_t apdulen,
+ pininfo_t *pininfo,
+ unsigned char *resp, size_t maxresplen, size_t *nresp);
+int ccid_transceive_escape (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *resp, size_t maxresplen,
+ size_t *nresp);
+int ccid_require_get_status (ccid_driver_t handle);
+
+
+#endif /*CCID_DRIVER_H*/
diff --git a/scd/command.c b/scd/command.c
new file mode 100644
index 0000000..925fd75
--- /dev/null
+++ b/scd/command.c
@@ -0,0 +1,2130 @@
+/* command.c - SCdaemon command handler
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef USE_NPTH
+# include <npth.h>
+#endif
+
+#include "scdaemon.h"
+#include <assuan.h>
+#include <ksba.h>
+#include "iso7816.h"
+#include "apdu.h" /* Required for apdu_*_reader (). */
+#include "atr.h"
+#include "../common/asshelp.h"
+#include "../common/server-help.h"
+
+/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
+#define MAXLEN_PIN 100
+
+/* Maximum allowed size of key data as used in inquiries. */
+#define MAXLEN_KEYDATA 4096
+
+/* Maximum allowed total data size for SETDATA. */
+#define MAXLEN_SETDATA 4096
+
+/* Maximum allowed size of certificate data as used in inquiries. */
+#define MAXLEN_CERTDATA 16384
+
+
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+#define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local)
+
+
+/* Data used to associate an Assuan context with local server data.
+ This object describes the local properties of one session. */
+struct server_local_s
+{
+ /* We keep a list of all active sessions with the anchor at
+ SESSION_LIST (see below). This field is used for linking. */
+ struct server_local_s *next_session;
+
+ /* This object is usually assigned to a CTRL object (which is
+ globally visible). While enumerating all sessions we sometimes
+ need to access data of the CTRL object; thus we keep a
+ backpointer here. */
+ ctrl_t ctrl_backlink;
+
+ /* The Assuan context used by this session/server. */
+ assuan_context_t assuan_ctx;
+
+#ifdef HAVE_W32_SYSTEM
+ void *event_signal; /* Or NULL if not used. */
+#else
+ int event_signal; /* Or 0 if not used. */
+#endif
+
+ /* True if the card has been removed and a reset is required to
+ continue operation. */
+ int card_removed;
+
+ /* If set to true we will be terminate ourself at the end of the
+ this session. */
+ int stopme;
+
+};
+
+
+/* To keep track of all running sessions, we link all active server
+ contexts and the anchor in this variable. */
+static struct server_local_s *session_list;
+
+/* If a session has been locked we store a link to its server object
+ in this variable. */
+static struct server_local_s *locked_session;
+
+
+/* Convert the STRING into a newly allocated buffer while translating
+ the hex numbers. Stops at the first invalid character. Blanks and
+ colons are allowed to separate the hex digits. Returns NULL on
+ error or a newly malloced buffer and its length in LENGTH. */
+static unsigned char *
+hex_to_buffer (const char *string, size_t *r_length)
+{
+ unsigned char *buffer;
+ const char *s;
+ size_t n;
+
+ buffer = xtrymalloc (strlen (string)+1);
+ if (!buffer)
+ return NULL;
+ for (s=string, n=0; *s; s++)
+ {
+ if (spacep (s) || *s == ':')
+ continue;
+ if (hexdigitp (s) && hexdigitp (s+1))
+ {
+ buffer[n++] = xtoi_2 (s);
+ s++;
+ }
+ else
+ break;
+ }
+ *r_length = n;
+ return buffer;
+}
+
+
+
+/* Reset the card and free the application context. With SEND_RESET
+ set to true actually send a RESET to the reader; this is the normal
+ way of calling the function. If KEEP_LOCK is set and the session
+ is locked that lock wil not be released. */
+static void
+do_reset (ctrl_t ctrl, int send_reset, int keep_lock)
+{
+ app_t app = ctrl->app_ctx;
+
+ if (app)
+ app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset);
+
+ /* If we hold a lock, unlock now. */
+ if (!keep_lock && locked_session && ctrl->server_local == locked_session)
+ {
+ locked_session = NULL;
+ log_info ("implicitly unlocking due to RESET\n");
+ }
+}
+
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ do_reset (ctrl, 1, has_option (line, "--keep-lock"));
+ return 0;
+}
+
+
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ if (!strcmp (key, "event-signal"))
+ {
+ /* A value of 0 is allowed to reset the event signal. */
+#ifdef HAVE_W32_SYSTEM
+ if (!*value)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+#ifdef _WIN64
+ ctrl->server_local->event_signal = (void *)strtoull (value, NULL, 16);
+#else
+ ctrl->server_local->event_signal = (void *)strtoul (value, NULL, 16);
+#endif
+#else
+ int i = *value? atoi (value) : -1;
+ if (i < 0)
+ return gpg_error (GPG_ERR_ASS_PARAMETER);
+ ctrl->server_local->event_signal = i;
+#endif
+ }
+
+ return 0;
+}
+
+
+/* If the card has not yet been opened, do it. */
+static gpg_error_t
+open_card (ctrl_t ctrl)
+{
+ /* If we ever got a card not present error code, return that. Only
+ the SERIALNO command and a reset are able to clear from that
+ state. */
+ if (ctrl->server_local->card_removed)
+ return gpg_error (GPG_ERR_CARD_REMOVED);
+
+ if ( IS_LOCKED (ctrl) )
+ return gpg_error (GPG_ERR_LOCKED);
+
+ if (ctrl->app_ctx)
+ return 0;
+
+ return select_application (ctrl, NULL, &ctrl->app_ctx, 0, NULL, 0);
+}
+
+/* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */
+static gpg_error_t
+open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno)
+{
+ gpg_error_t err;
+ unsigned char *serialno_bin = NULL;
+ size_t serialno_bin_len = 0;
+ app_t app = ctrl->app_ctx;
+
+ /* If we are already initialized for one specific application we
+ need to check that the client didn't requested a specific
+ application different from the one in use before we continue. */
+ if (apptype && ctrl->app_ctx)
+ return check_application_conflict (apptype, ctrl->app_ctx);
+
+ /* Re-scan USB devices. Release APP, before the scan. */
+ ctrl->app_ctx = NULL;
+ release_application (app, 0);
+
+ if (serialno)
+ serialno_bin = hex_to_buffer (serialno, &serialno_bin_len);
+
+ err = select_application (ctrl, apptype, &ctrl->app_ctx, 1,
+ serialno_bin, serialno_bin_len);
+ xfree (serialno_bin);
+
+ return err;
+}
+
+
+static const char hlp_serialno[] =
+ "SERIALNO [--demand=<serialno>] [<apptype>]\n"
+ "\n"
+ "Return the serial number of the card using a status response. This\n"
+ "function should be used to check for the presence of a card.\n"
+ "\n"
+ "If --demand is given, an application on the card with SERIALNO is\n"
+ "selected and an error is returned if no such card available.\n"
+ "\n"
+ "If APPTYPE is given, an application of that type is selected and an\n"
+ "error is returned if the application is not supported or available.\n"
+ "The default is to auto-select the application using a hardwired\n"
+ "preference system. Note, that a future extension to this function\n"
+ "may enable specifying a list and order of applications to try.\n"
+ "\n"
+ "This function is special in that it can be used to reset the card.\n"
+ "Most other functions will return an error when a card change has\n"
+ "been detected and the use of this function is therefore required.\n"
+ "\n"
+ "Background: We want to keep the client clear of handling card\n"
+ "changes between operations; i.e. the client can assume that all\n"
+ "operations are done on the same card unless he calls this function.";
+static gpg_error_t
+cmd_serialno (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ struct server_local_s *sl;
+ int rc = 0;
+ char *serial;
+ const char *demand;
+
+ if ( IS_LOCKED (ctrl) )
+ return gpg_error (GPG_ERR_LOCKED);
+
+ if ((demand = has_option_name (line, "--demand")))
+ {
+ if (*demand != '=')
+ return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option");
+ line = (char *)++demand;
+ for (; *line && !spacep (line); line++)
+ ;
+ if (*line)
+ *line++ = 0;
+ }
+ else
+ demand = NULL;
+
+ line = skip_options (line);
+
+ /* Clear the remove flag so that the open_card is able to reread it. */
+ if (ctrl->server_local->card_removed)
+ ctrl->server_local->card_removed = 0;
+
+ if ((rc = open_card_with_request (ctrl, *line? line:NULL, demand)))
+ {
+ ctrl->server_local->card_removed = 1;
+ return rc;
+ }
+
+ /* Success, clear the card_removed flag for all sessions. */
+ for (sl=session_list; sl; sl = sl->next_session)
+ {
+ ctrl_t c = sl->ctrl_backlink;
+
+ if (c != ctrl)
+ c->server_local->card_removed = 0;
+ }
+
+ serial = app_get_serialno (ctrl->app_ctx);
+ if (!serial)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ rc = assuan_write_status (ctx, "SERIALNO", serial);
+ xfree (serial);
+ return rc;
+}
+
+
+static const char hlp_learn[] =
+ "LEARN [--force] [--keypairinfo]\n"
+ "\n"
+ "Learn all useful information of the currently inserted card. When\n"
+ "used without the force options, the command might do an INQUIRE\n"
+ "like this:\n"
+ "\n"
+ " INQUIRE KNOWNCARDP <hexstring_with_serialNumber>\n"
+ "\n"
+ "The client should just send an \"END\" if the processing should go on\n"
+ "or a \"CANCEL\" to force the function to terminate with a Cancel\n"
+ "error message.\n"
+ "\n"
+ "With the option --keypairinfo only KEYPARIINFO status lines are\n"
+ "returned.\n"
+ "\n"
+ "The response of this command is a list of status lines formatted as\n"
+ "this:\n"
+ "\n"
+ " S APPTYPE <apptype>\n"
+ "\n"
+ "This returns the type of the application, currently the strings:\n"
+ "\n"
+ " P15 = PKCS-15 structure used\n"
+ " DINSIG = DIN SIG\n"
+ " OPENPGP = OpenPGP card\n"
+ " PIV = PIV card\n"
+ " NKS = NetKey card\n"
+ "\n"
+ "are implemented. These strings are aliases for the AID\n"
+ "\n"
+ " S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>\n"
+ "\n"
+ "If there is no certificate yet stored on the card a single 'X' is\n"
+ "returned as the keygrip. In addition to the keypair info, information\n"
+ "about all certificates stored on the card is also returned:\n"
+ "\n"
+ " S CERTINFO <certtype> <hexstring_with_id>\n"
+ "\n"
+ "Where CERTTYPE is a number indicating the type of certificate:\n"
+ " 0 := Unknown\n"
+ " 100 := Regular X.509 cert\n"
+ " 101 := Trusted X.509 cert\n"
+ " 102 := Useful X.509 cert\n"
+ " 110 := Root CA cert in a special format (e.g. DINSIG)\n"
+ " 111 := Root CA cert as standard X509 cert.\n"
+ "\n"
+ "For certain cards, more information will be returned:\n"
+ "\n"
+ " S KEY-FPR <no> <hexstring>\n"
+ "\n"
+ "For OpenPGP cards this returns the stored fingerprints of the\n"
+ "keys. This can be used check whether a key is available on the\n"
+ "card. NO may be 1, 2 or 3.\n"
+ "\n"
+ " S CA-FPR <no> <hexstring>\n"
+ "\n"
+ "Similar to above, these are the fingerprints of keys assumed to be\n"
+ "ultimately trusted.\n"
+ "\n"
+ " S DISP-NAME <name_of_card_holder>\n"
+ "\n"
+ "The name of the card holder as stored on the card; percent\n"
+ "escaping takes place, spaces are encoded as '+'\n"
+ "\n"
+ " S PUBKEY-URL <url>\n"
+ "\n"
+ "The URL to be used for locating the entire public key.\n"
+ " \n"
+ "Note, that this function may even be used on a locked card.";
+static gpg_error_t
+cmd_learn (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+ int only_keypairinfo = has_option (line, "--keypairinfo");
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ /* Unless the force option is used we try a shortcut by identifying
+ the card using a serial number and inquiring the client with
+ that. The client may choose to cancel the operation if he already
+ knows about this card */
+ if (!only_keypairinfo)
+ {
+ const char *reader;
+ char *serial;
+ app_t app = ctrl->app_ctx;
+
+ if (!app)
+ return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+
+ reader = apdu_get_reader_name (app->slot);
+ if (!reader)
+ return out_of_core ();
+ send_status_direct (ctrl, "READER", reader);
+ /* No need to free the string of READER. */
+
+ serial = app_get_serialno (ctrl->app_ctx);
+ if (!serial)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ rc = assuan_write_status (ctx, "SERIALNO", serial);
+ if (rc < 0)
+ {
+ xfree (serial);
+ return out_of_core ();
+ }
+
+ if (!has_option (line, "--force"))
+ {
+ char *command;
+
+ rc = gpgrt_asprintf (&command, "KNOWNCARDP %s", serial);
+ if (rc < 0)
+ {
+ xfree (serial);
+ return out_of_core ();
+ }
+ rc = assuan_inquire (ctx, command, NULL, NULL, 0);
+ xfree (command);
+ if (rc)
+ {
+ if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED)
+ log_error ("inquire KNOWNCARDP failed: %s\n",
+ gpg_strerror (rc));
+ xfree (serial);
+ return rc;
+ }
+ /* Not canceled, so we have to proceed. */
+ }
+ xfree (serial);
+ }
+
+ /* Let the application print out its collection of useful status
+ information. */
+ if (!rc)
+ rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo);
+
+ return rc;
+}
+
+
+
+static const char hlp_readcert[] =
+ "READCERT <hexified_certid>|<keyid>\n"
+ "\n"
+ "Note, that this function may even be used on a locked card.";
+static gpg_error_t
+cmd_readcert (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *cert;
+ size_t ncert;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ line = xstrdup (line); /* Need a copy of the line. */
+ rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert);
+ if (rc)
+ log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+ xfree (line);
+ line = NULL;
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, cert, ncert);
+ xfree (cert);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+
+static const char hlp_readkey[] =
+ "READKEY [--advanced] [--info[-only]] <keyid>\n"
+ "\n"
+ "Return the public key for the given cert or key ID as a standard\n"
+ "S-expression. With --advanced the S-expression is returned in\n"
+ "advanced format. With --info a KEYPAIRINFO status line is also\n"
+ "emitted; with --info-only the regular output is suppressed.";
+static gpg_error_t
+cmd_readkey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ int advanced = 0;
+ int opt_info = 0;
+ int opt_nokey = 0;
+ unsigned char *cert = NULL;
+ size_t ncert;
+ unsigned char *pk;
+ size_t pklen;
+ int direct_readkey = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (has_option (line, "--advanced"))
+ advanced = 1;
+ if (has_option (line, "--info"))
+ opt_info = 1;
+ if (has_option (line, "--info-only"))
+ opt_info = opt_nokey = 1;
+
+ line = skip_options (line);
+ line = xstrdup (line); /* Need a copy of the line. */
+
+ /* If the application supports the READKEY function we use that.
+ Otherwise we use the old way by extracting it from the
+ certificate. */
+ rc = app_readkey (ctrl->app_ctx, ctrl, advanced, line, &pk, &pklen);
+ if (!rc)
+ direct_readkey = 1; /* Yeah, got that key - send it back. */
+ else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION
+ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+ {
+ /* Fall back to certificate reading. */
+ rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert);
+ if (rc)
+ log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+ else
+ {
+ rc = app_help_pubkey_from_cert (cert, ncert, &pk, &pklen);
+ if (rc)
+ log_error ("failed to parse the certificate: %s\n",
+ gpg_strerror (rc));
+ }
+ }
+ else
+ log_error ("app_readkey failed: %s\n", gpg_strerror (rc));
+
+ if (!rc && pk && pklen && opt_info && !direct_readkey)
+ {
+ char keygripstr[KEYGRIP_LEN*2+1];
+ char *algostr;
+
+ rc = app_help_get_keygrip_string_pk (pk, pklen,
+ keygripstr, NULL, NULL,
+ &algostr);
+ if (rc)
+ {
+ log_error ("app_help_get_keygrip_string failed: %s\n",
+ gpg_strerror (rc));
+ goto leave;
+ }
+
+ /* FIXME: Using LINE is not correct because it might be an
+ * OID and has not been canonicalized (i.e. uppercased). */
+ send_status_info (ctrl, "KEYPAIRINFO",
+ keygripstr, strlen (keygripstr),
+ line, strlen (line),
+ "-", (size_t)1,
+ "-", (size_t)1,
+ algostr, strlen (algostr),
+ NULL, (size_t)0);
+ xfree (algostr);
+ }
+
+
+ if (!rc && pk && pklen && !opt_nokey)
+ rc = assuan_send_data (ctx, pk, pklen);
+
+ leave:
+ xfree (cert);
+ xfree (pk);
+ xfree (line);
+ return rc;
+}
+
+
+
+static const char hlp_setdata[] =
+ "SETDATA [--append] <hexstring>\n"
+ "\n"
+ "The client should use this command to tell us the data he want to sign.\n"
+ "With the option --append, the data is appended to the data set by a\n"
+ "previous SETDATA command.";
+static gpg_error_t
+cmd_setdata (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int append;
+ int n, i, off;
+ char *p;
+ unsigned char *buf;
+
+ append = (ctrl->in_data.value && has_option (line, "--append"));
+
+ line = skip_options (line);
+
+ if (locked_session && locked_session != ctrl->server_local)
+ return gpg_error (GPG_ERR_LOCKED);
+
+ /* Parse the hexstring. */
+ for (p=line,n=0; hexdigitp (p); p++, n++)
+ ;
+ if (*p)
+ return set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring");
+ if (!n)
+ return set_error (GPG_ERR_ASS_PARAMETER, "no data given");
+ if ((n&1))
+ return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits");
+ n /= 2;
+ if (append)
+ {
+ if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA)
+ return set_error (GPG_ERR_TOO_LARGE,
+ "limit on total size of data reached");
+ buf = xtrymalloc (ctrl->in_data.valuelen + n);
+ }
+ else
+ buf = xtrymalloc (n);
+ if (!buf)
+ return out_of_core ();
+
+ if (append)
+ {
+ memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen);
+ off = ctrl->in_data.valuelen;
+ }
+ else
+ off = 0;
+ for (p=line, i=0; i < n; p += 2, i++)
+ buf[off+i] = xtoi_2 (p);
+
+ xfree (ctrl->in_data.value);
+ ctrl->in_data.value = buf;
+ ctrl->in_data.valuelen = off+n;
+ return 0;
+}
+
+
+
+static gpg_error_t
+pin_cb (void *opaque, const char *info, char **retstr)
+{
+ assuan_context_t ctx = opaque;
+ char *command;
+ int rc;
+ unsigned char *value;
+ size_t valuelen;
+
+ if (!retstr)
+ {
+ /* We prompt for pinpad entry. To make sure that the popup has
+ been show we use an inquire and not just a status message.
+ We ignore any value returned. */
+ if (info)
+ {
+ log_debug ("prompting for pinpad entry '%s'\n", info);
+ rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info);
+ if (rc < 0)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ xfree (command);
+ }
+ else
+ {
+ log_debug ("dismiss pinpad entry prompt\n");
+ rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT",
+ &value, &valuelen, MAXLEN_PIN);
+ }
+ if (!rc)
+ xfree (value);
+ return rc;
+ }
+
+ *retstr = NULL;
+ log_debug ("asking for PIN '%s'\n", info);
+
+ rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
+ if (rc < 0)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ /* Fixme: Write an inquire function which returns the result in
+ secure memory and check all further handling of the PIN. */
+ assuan_begin_confidential (ctx);
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ assuan_end_confidential (ctx);
+ xfree (command);
+ if (rc)
+ return rc;
+
+ if (!valuelen || value[valuelen-1])
+ {
+ /* We require that the returned value is an UTF-8 string */
+ xfree (value);
+ return gpg_error (GPG_ERR_INV_RESPONSE);
+ }
+ *retstr = (char*)value;
+ return 0;
+}
+
+
+static const char hlp_pksign[] =
+ "PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>\n"
+ "\n"
+ "The --hash option is optional; the default is SHA1.";
+static gpg_error_t
+cmd_pksign (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+ int hash_algo;
+
+ if (has_option (line, "--hash=rmd160"))
+ hash_algo = GCRY_MD_RMD160;
+ else if (has_option (line, "--hash=sha1"))
+ hash_algo = GCRY_MD_SHA1;
+ else if (has_option (line, "--hash=sha224"))
+ hash_algo = GCRY_MD_SHA224;
+ else if (has_option (line, "--hash=sha256"))
+ hash_algo = GCRY_MD_SHA256;
+ else if (has_option (line, "--hash=sha384"))
+ hash_algo = GCRY_MD_SHA384;
+ else if (has_option (line, "--hash=sha512"))
+ hash_algo = GCRY_MD_SHA512;
+ else if (has_option (line, "--hash=md5"))
+ hash_algo = GCRY_MD_MD5;
+ else if (!strstr (line, "--"))
+ hash_algo = GCRY_MD_SHA1;
+ else
+ return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
+
+ line = skip_options (line);
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ /* We have to use a copy of the key ID because the function may use
+ the pin_cb which in turn uses the assuan line buffer and thus
+ overwriting the original line with the keyid */
+ keyidstr = xtrystrdup (line);
+ if (!keyidstr)
+ return out_of_core ();
+
+ rc = app_sign (ctrl->app_ctx, ctrl,
+ keyidstr, hash_algo,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+
+ xfree (keyidstr);
+ if (rc)
+ {
+ log_error ("app_sign failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return rc;
+}
+
+
+static const char hlp_pkauth[] =
+ "PKAUTH <hexified_id>";
+static gpg_error_t
+cmd_pkauth (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* We have to use a copy of the key ID because the function may use
+ the pin_cb which in turn uses the assuan line buffer and thus
+ overwriting the original line with the keyid */
+ keyidstr = xtrystrdup (line);
+ if (!keyidstr)
+ return out_of_core ();
+
+ rc = app_auth (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ xfree (keyidstr);
+ if (rc)
+ {
+ log_error ("app_auth failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return rc;
+}
+
+
+static const char hlp_pkdecrypt[] =
+ "PKDECRYPT <hexified_id>";
+static gpg_error_t
+cmd_pkdecrypt (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ unsigned char *outdata;
+ size_t outdatalen;
+ char *keyidstr;
+ unsigned int infoflags;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyidstr = xtrystrdup (line);
+ if (!keyidstr)
+ return out_of_core ();
+ rc = app_decipher (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen, &infoflags);
+
+ xfree (keyidstr);
+ if (rc)
+ {
+ log_error ("app_decipher failed: %s\n", gpg_strerror (rc));
+ }
+ else
+ {
+ /* If the card driver told us that there is no padding, send a
+ status line. If there is a padding it is assumed that the
+ caller knows what padding is used. It would have been better
+ to always send that information but for backward
+ compatibility we can't do that. */
+ if ((infoflags & APP_DECIPHER_INFO_NOPAD))
+ send_status_direct (ctrl, "PADDING", "0");
+ rc = assuan_send_data (ctx, outdata, outdatalen);
+ xfree (outdata);
+ if (rc)
+ return rc; /* that is already an assuan error code */
+ }
+
+ return rc;
+}
+
+
+static const char hlp_getattr[] =
+ "GETATTR <name>\n"
+ "\n"
+ "This command is used to retrieve data from a smartcard. The\n"
+ "allowed names depend on the currently selected smartcard\n"
+ "application. NAME must be percent and '+' escaped. The value is\n"
+ "returned through status message, see the LEARN command for details.\n"
+ "\n"
+ "However, the current implementation assumes that Name is not escaped;\n"
+ "this works as long as no one uses arbitrary escaping. \n"
+ "\n"
+ "Note, that this function may even be used on a locked card.";
+static gpg_error_t
+cmd_getattr (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ const char *keyword;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyword = line;
+ for (; *line && !spacep (line); line++)
+ ;
+ if (*line)
+ *line++ = 0;
+
+ /* (We ignore any garbage for now.) */
+
+ /* FIXME: Applications should not return sensitive data if the card
+ is locked. */
+ rc = app_getattr (ctrl->app_ctx, ctrl, keyword);
+
+ return rc;
+}
+
+
+static const char hlp_setattr[] =
+ "SETATTR <name> <value> \n"
+ "\n"
+ "This command is used to store data on a smartcard. The allowed\n"
+ "names and values are depend on the currently selected smartcard\n"
+ "application. NAME and VALUE must be percent and '+' escaped.\n"
+ "\n"
+ "However, the current implementation assumes that NAME is not\n"
+ "escaped; this works as long as no one uses arbitrary escaping.\n"
+ "\n"
+ "A PIN will be requested for most NAMEs. See the corresponding\n"
+ "setattr function of the actually used application (app-*.c) for\n"
+ "details.";
+static gpg_error_t
+cmd_setattr (assuan_context_t ctx, char *orig_line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyword;
+ int keywordlen;
+ size_t nbytes;
+ char *line, *linebuf;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ /* We need to use a copy of LINE, because PIN_CB uses the same
+ context and thus reuses the Assuan provided LINE. */
+ line = linebuf = xtrystrdup (orig_line);
+ if (!line)
+ return out_of_core ();
+
+ keyword = line;
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ if (*line)
+ *line++ = 0;
+ while (spacep (line))
+ line++;
+ nbytes = percent_plus_unescape_inplace (line, 0);
+
+ rc = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx,
+ (const unsigned char*)line, nbytes);
+ xfree (linebuf);
+
+ return rc;
+}
+
+
+static const char hlp_writecert[] =
+ "WRITECERT <hexified_certid>\n"
+ "\n"
+ "This command is used to store a certifciate on a smartcard. The\n"
+ "allowed certids depend on the currently selected smartcard\n"
+ "application. The actual certifciate is requested using the inquiry\n"
+ "\"CERTDATA\" and needs to be provided in its raw (e.g. DER) form.\n"
+ "\n"
+ "In almost all cases a PIN will be requested. See the related\n"
+ "writecert function of the actually used application (app-*.c) for\n"
+ "details.";
+static gpg_error_t
+cmd_writecert (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *certid;
+ unsigned char *certdata;
+ size_t certdatalen;
+
+ line = skip_options (line);
+
+ if (!*line)
+ return set_error (GPG_ERR_ASS_PARAMETER, "no certid given");
+ certid = line;
+ while (*line && !spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ certid = xtrystrdup (certid);
+ if (!certid)
+ return out_of_core ();
+
+ /* Now get the actual keydata. */
+ rc = assuan_inquire (ctx, "CERTDATA",
+ &certdata, &certdatalen, MAXLEN_CERTDATA);
+ if (rc)
+ {
+ xfree (certid);
+ return rc;
+ }
+
+ /* Write the certificate to the card. */
+ rc = app_writecert (ctrl->app_ctx, ctrl, certid,
+ pin_cb, ctx, certdata, certdatalen);
+ xfree (certid);
+ xfree (certdata);
+
+ return rc;
+}
+
+
+static const char hlp_writekey[] =
+ "WRITEKEY [--force] <keyid> \n"
+ "\n"
+ "This command is used to store a secret key on a smartcard. The\n"
+ "allowed keyids depend on the currently selected smartcard\n"
+ "application. The actual keydata is requested using the inquiry\n"
+ "\"KEYDATA\" and need to be provided without any protection. With\n"
+ "--force set an existing key under this KEYID will get overwritten.\n"
+ "The keydata is expected to be the usual canonical encoded\n"
+ "S-expression.\n"
+ "\n"
+ "A PIN will be requested for most NAMEs. See the corresponding\n"
+ "writekey function of the actually used application (app-*.c) for\n"
+ "details.";
+static gpg_error_t
+cmd_writekey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyid;
+ int force = has_option (line, "--force");
+ unsigned char *keydata;
+ size_t keydatalen;
+
+ line = skip_options (line);
+
+ if (!*line)
+ return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given");
+ keyid = line;
+ while (*line && !spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ keyid = xtrystrdup (keyid);
+ if (!keyid)
+ return out_of_core ();
+
+ /* Now get the actual keydata. */
+ assuan_begin_confidential (ctx);
+ rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
+ assuan_end_confidential (ctx);
+ if (rc)
+ {
+ xfree (keyid);
+ return rc;
+ }
+
+ /* Write the key to the card. */
+ rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0,
+ pin_cb, ctx, keydata, keydatalen);
+ xfree (keyid);
+ xfree (keydata);
+
+ return rc;
+}
+
+
+static const char hlp_genkey[] =
+ "GENKEY [--force] [--timestamp=<isodate>] <no>\n"
+ "\n"
+ "Generate a key on-card identified by NO, which is application\n"
+ "specific. Return values are application specific. For OpenPGP\n"
+ "cards 3 status lines are returned:\n"
+ "\n"
+ " S KEY-FPR <hexstring>\n"
+ " S KEY-CREATED-AT <seconds_since_epoch>\n"
+ " S KEY-DATA [-|p|n] <hexdata>\n"
+ "\n"
+ " 'p' and 'n' are the names of the RSA parameters; '-' is used to\n"
+ " indicate that HEXDATA is the first chunk of a parameter given\n"
+ " by the next KEY-DATA.\n"
+ "\n"
+ "--force is required to overwrite an already existing key. The\n"
+ "KEY-CREATED-AT is required for further processing because it is\n"
+ "part of the hashed key material for the fingerprint.\n"
+ "\n"
+ "If --timestamp is given an OpenPGP key will be created using this\n"
+ "value. The value needs to be in ISO Format; e.g.\n"
+ "\"--timestamp=20030316T120000\" and after 1970-01-01 00:00:00.\n"
+ "\n"
+ "The public part of the key can also later be retrieved using the\n"
+ "READKEY command.";
+static gpg_error_t
+cmd_genkey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *save_line;
+ int force;
+ const char *s;
+ time_t timestamp;
+
+ force = has_option (line, "--force");
+
+ if ((s=has_option_name (line, "--timestamp")))
+ {
+ if (*s != '=')
+ return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option");
+ timestamp = isotime2epoch (s+1);
+ if (timestamp < 1)
+ return set_error (GPG_ERR_ASS_PARAMETER, "invalid time value");
+ }
+ else
+ timestamp = 0;
+
+
+ line = skip_options (line);
+ if (!*line)
+ {
+ rc = set_error (GPG_ERR_ASS_PARAMETER, "no key number given");
+ goto leave;
+ }
+ save_line = line;
+ while (*line && !spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ goto leave;
+
+ if (!ctrl->app_ctx)
+ {
+ rc = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ goto leave;
+ }
+
+ {
+ char *tmp = xtrystrdup (save_line);
+ if (!tmp)
+ return gpg_error_from_syserror ();
+ rc = app_genkey (ctrl->app_ctx, ctrl, tmp, NULL,
+ force? APP_GENKEY_FLAG_FORCE : 0,
+ timestamp, pin_cb, ctx);
+ xfree (tmp);
+ }
+
+ leave:
+ return rc;
+}
+
+
+static const char hlp_random[] =
+ "RANDOM <nbytes>\n"
+ "\n"
+ "Get NBYTES of random from the card and send them back as data.\n"
+ "This usually involves EEPROM write on the card and thus excessive\n"
+ "use of this command may destroy the card.\n"
+ "\n"
+ "Note, that this function may be even be used on a locked card.";
+static gpg_error_t
+cmd_random (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ size_t nbytes;
+ unsigned char *buffer;
+
+ if (!*line)
+ return set_error (GPG_ERR_ASS_PARAMETER,
+ "number of requested bytes missing");
+ nbytes = strtoul (line, NULL, 0);
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ buffer = xtrymalloc (nbytes);
+ if (!buffer)
+ return out_of_core ();
+
+ rc = app_get_challenge (ctrl->app_ctx, ctrl, nbytes, buffer);
+ if (!rc)
+ {
+ rc = assuan_send_data (ctx, buffer, nbytes);
+ xfree (buffer);
+ return rc; /* that is already an assuan error code */
+ }
+ xfree (buffer);
+
+ return rc;
+}
+
+
+
+static const char hlp_passwd[] =
+ "PASSWD [--reset] [--nullpin] [--clear] <chvno>\n"
+ "\n"
+ "Change the PIN or, if --reset is given, reset the retry counter of\n"
+ "the card holder verification vector CHVNO. The option --nullpin is\n"
+ "used for TCOS cards to set the initial PIN. The option --clear clears\n"
+ "the security status associated with the PIN so that the PIN needs to\n"
+ "be presented again. The format of CHVNO depends on the card application.";
+static gpg_error_t
+cmd_passwd (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *chvnostr;
+ unsigned int flags = 0;
+
+ if (has_option (line, "--reset"))
+ flags |= APP_CHANGE_FLAG_RESET;
+ if (has_option (line, "--nullpin"))
+ flags |= APP_CHANGE_FLAG_NULLPIN;
+ if (has_option (line, "--clear"))
+ flags |= APP_CHANGE_FLAG_CLEAR;
+
+ line = skip_options (line);
+
+ if (!*line)
+ return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given");
+ chvnostr = line;
+ while (*line && !spacep (line))
+ line++;
+ *line = 0;
+
+ /* Do not allow other flags aside of --clear. */
+ if ((flags & APP_CHANGE_FLAG_CLEAR) && (flags & ~APP_CHANGE_FLAG_CLEAR))
+ return set_error (GPG_ERR_UNSUPPORTED_OPERATION,
+ "--clear used with other options");
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ chvnostr = xtrystrdup (chvnostr);
+ if (!chvnostr)
+ return out_of_core ();
+ rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, flags, pin_cb, ctx);
+ if (rc)
+ log_error ("command passwd failed: %s\n", gpg_strerror (rc));
+ xfree (chvnostr);
+
+ return rc;
+}
+
+
+static const char hlp_checkpin[] =
+ "CHECKPIN <idstr>\n"
+ "\n"
+ "Perform a VERIFY operation without doing anything else. This may\n"
+ "be used to initialize a the PIN cache earlier to long lasting\n"
+ "operations. Its use is highly application dependent.\n"
+ "\n"
+ "For OpenPGP:\n"
+ "\n"
+ " Perform a simple verify operation for CHV1 and CHV2, so that\n"
+ " further operations won't ask for CHV2 and it is possible to do a\n"
+ " cheap check on the PIN: If there is something wrong with the PIN\n"
+ " entry system, only the regular CHV will get blocked and not the\n"
+ " dangerous CHV3. IDSTR is the usual card's serial number in hex\n"
+ " notation; an optional fingerprint part will get ignored. There\n"
+ " is however a special mode if the IDSTR is suffixed with the\n"
+ " literal string \"[CHV3]\": In this case the Admin PIN is checked\n"
+ " if and only if the retry counter is still at 3.\n"
+ "\n"
+ "For Netkey:\n"
+ "\n"
+ " Any of the valid PIN Ids may be used. These are the strings:\n"
+ "\n"
+ " PW1.CH - Global password 1\n"
+ " PW2.CH - Global password 2\n"
+ " PW1.CH.SIG - SigG password 1\n"
+ " PW2.CH.SIG - SigG password 2\n"
+ "\n"
+ " For a definitive list, see the implementation in app-nks.c.\n"
+ " Note that we call a PW2.* PIN a \"PUK\" despite that since TCOS\n"
+ " 3.0 they are technically alternative PINs used to mutally\n"
+ " unblock each other.";
+static gpg_error_t
+cmd_checkpin (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *idstr;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ /* We have to use a copy of the key ID because the function may use
+ the pin_cb which in turn uses the assuan line buffer and thus
+ overwriting the original line with the keyid. */
+ idstr = xtrystrdup (line);
+ if (!idstr)
+ return out_of_core ();
+
+ rc = app_check_pin (ctrl->app_ctx, ctrl, idstr, pin_cb, ctx);
+ xfree (idstr);
+ if (rc)
+ log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
+
+ return rc;
+}
+
+
+static const char hlp_lock[] =
+ "LOCK [--wait]\n"
+ "\n"
+ "Grant exclusive card access to this session. Note that there is\n"
+ "no lock counter used and a second lock from the same session will\n"
+ "be ignored. A single unlock (or RESET) unlocks the session.\n"
+ "Return GPG_ERR_LOCKED if another session has locked the reader.\n"
+ "\n"
+ "If the option --wait is given the command will wait until a\n"
+ "lock has been released.";
+static gpg_error_t
+cmd_lock (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+
+ retry:
+ if (locked_session)
+ {
+ if (locked_session != ctrl->server_local)
+ rc = gpg_error (GPG_ERR_LOCKED);
+ }
+ else
+ locked_session = ctrl->server_local;
+
+#ifdef USE_NPTH
+ if (rc && has_option (line, "--wait"))
+ {
+ rc = 0;
+ npth_sleep (1); /* Better implement an event mechanism. However,
+ for card operations this should be
+ sufficient. */
+ /* Send a progress so that we can detect a connection loss. */
+ rc = send_status_printf (ctrl, "PROGRESS", "scd_locked . 0 0");
+ if (!rc)
+ goto retry;
+ }
+#endif /*USE_NPTH*/
+
+ if (rc)
+ log_error ("cmd_lock failed: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+static const char hlp_unlock[] =
+ "UNLOCK\n"
+ "\n"
+ "Release exclusive card access.";
+static gpg_error_t
+cmd_unlock (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc = 0;
+
+ (void)line;
+
+ if (locked_session)
+ {
+ if (locked_session != ctrl->server_local)
+ rc = gpg_error (GPG_ERR_LOCKED);
+ else
+ locked_session = NULL;
+ }
+ else
+ rc = gpg_error (GPG_ERR_NOT_LOCKED);
+
+ if (rc)
+ log_error ("cmd_unlock failed: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Ease reading of Assuan data ;ines by sending a physical line after
+ * each LF. */
+static gpg_error_t
+pretty_assuan_send_data (assuan_context_t ctx,
+ const void *buffer_arg, size_t size)
+{
+ const char *buffer = buffer_arg;
+ const char *p;
+ size_t n, nbytes;
+ gpg_error_t err;
+
+ nbytes = size;
+ do
+ {
+ p = memchr (buffer, '\n', nbytes);
+ n = p ? (p - buffer) + 1 : nbytes;
+ err = assuan_send_data (ctx, buffer, n);
+ if (err)
+ {
+ /* We also set ERRNO in case this function is used by a
+ * custom estream I/O handler. */
+ gpg_err_set_errno (EIO);
+ goto leave;
+ }
+ buffer += n;
+ nbytes -= n;
+ if (nbytes && (err=assuan_send_data (ctx, NULL, 0))) /* Flush line. */
+ {
+ gpg_err_set_errno (EIO);
+ goto leave;
+ }
+ }
+ while (nbytes);
+
+ leave:
+ return err;
+}
+
+
+static const char hlp_getinfo[] =
+ "GETINFO <what>\n"
+ "\n"
+ "Multi purpose command to return certain information. \n"
+ "Supported values of WHAT are:\n"
+ "\n"
+ " version - Return the version of the program.\n"
+ " pid - Return the process id of the server.\n"
+ " socket_name - Return the name of the socket.\n"
+ " connections - Return number of active connections.\n"
+ " status - Return the status of the current reader (in the future,\n"
+ " may also return the status of all readers). The status\n"
+ " is a list of one-character flags. The following flags\n"
+ " are currently defined:\n"
+ " 'u' Usable card present.\n"
+ " 'r' Card removed. A reset is necessary.\n"
+ " These flags are exclusive.\n"
+ " reader_list - Return a list of detected card readers.\n"
+ " deny_admin - Returns OK if admin commands are not allowed or\n"
+ " GPG_ERR_GENERAL if admin commands are allowed.\n"
+ " app_list - Return a list of supported applications. One\n"
+ " application per line, fields delimited by colons,\n"
+ " first field is the name.\n"
+ " card_list - Return a list of serial numbers of active cards,\n"
+ " using a status response.";
+static gpg_error_t
+cmd_getinfo (assuan_context_t ctx, char *line)
+{
+ int rc = 0;
+
+ if (!strcmp (line, "version"))
+ {
+ const char *s = VERSION;
+ rc = assuan_send_data (ctx, s, strlen (s));
+ }
+ else if (!strcmp (line, "pid"))
+ {
+ char numbuf[50];
+
+ snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
+ rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strcmp (line, "socket_name"))
+ {
+ const char *s = scd_get_socket_name ();
+
+ if (s)
+ rc = assuan_send_data (ctx, s, strlen (s));
+ else
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ }
+ else if (!strcmp (line, "connections"))
+ {
+ char numbuf[20];
+
+ snprintf (numbuf, sizeof numbuf, "%d", get_active_connection_count ());
+ rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
+ }
+ else if (!strcmp (line, "status"))
+ {
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ char flag;
+
+ if (open_card (ctrl))
+ flag = 'r';
+ else
+ flag = 'u';
+
+ rc = assuan_send_data (ctx, &flag, 1);
+ }
+ else if (!strcmp (line, "reader_list"))
+ {
+ char *s = apdu_get_reader_list ();
+ if (s)
+ rc = pretty_assuan_send_data (ctx, s, strlen (s));
+ else
+ rc = gpg_error (GPG_ERR_NO_DATA);
+ xfree (s);
+ }
+ else if (!strcmp (line, "deny_admin"))
+ rc = opt.allow_admin? gpg_error (GPG_ERR_GENERAL) : 0;
+ else if (!strcmp (line, "app_list"))
+ {
+ char *s = get_supported_applications ();
+ if (s)
+ rc = assuan_send_data (ctx, s, strlen (s));
+ else
+ rc = 0;
+ xfree (s);
+ }
+ else if (!strcmp (line, "card_list"))
+ {
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ app_send_card_list (ctrl);
+ }
+ else
+ rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
+ return rc;
+}
+
+
+static const char hlp_restart[] =
+ "RESTART\n"
+ "\n"
+ "Restart the current connection; this is a kind of warm reset. It\n"
+ "deletes the context used by this connection but does not send a\n"
+ "RESET to the card. Thus the card itself won't get reset. \n"
+ "\n"
+ "This is used by gpg-agent to reuse a primary pipe connection and\n"
+ "may be used by clients to backup from a conflict in the serial\n"
+ "command; i.e. to select another application.";
+static gpg_error_t
+cmd_restart (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ app_t app = ctrl->app_ctx;
+
+ (void)line;
+
+ if (app)
+ {
+ ctrl->app_ctx = NULL;
+ release_application (app, 0);
+ }
+ if (locked_session && ctrl->server_local == locked_session)
+ {
+ locked_session = NULL;
+ log_info ("implicitly unlocking due to RESTART\n");
+ }
+ return 0;
+}
+
+
+static const char hlp_disconnect[] =
+ "DISCONNECT\n"
+ "\n"
+ "Disconnect the card if the backend supports a disconnect operation.";
+static gpg_error_t
+cmd_disconnect (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ apdu_disconnect (ctrl->app_ctx->slot);
+ return 0;
+}
+
+
+
+static const char hlp_apdu[] =
+ "APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]\n"
+ "\n"
+ "Send an APDU to the current reader. This command bypasses the high\n"
+ "level functions and sends the data directly to the card. HEXSTRING\n"
+ "is expected to be a proper APDU. If HEXSTRING is not given no\n"
+ "commands are set to the card but the command will implictly check\n"
+ "whether the card is ready for use. \n"
+ "\n"
+ "Using the option \"--atr\" returns the ATR of the card as a status\n"
+ "message before any data like this:\n"
+ " S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1\n"
+ "\n"
+ "Using the option --more handles the card status word MORE_DATA\n"
+ "(61xx) and concatenates all responses to one block.\n"
+ "\n"
+ "Using the option \"--exlen\" the returned APDU may use extended\n"
+ "length up to N bytes. If N is not given a default value is used\n"
+ "(currently 4096).";
+static gpg_error_t
+cmd_apdu (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ app_t app;
+ int rc;
+ unsigned char *apdu;
+ size_t apdulen;
+ int with_atr;
+ int handle_more;
+ const char *s;
+ size_t exlen;
+
+ if (has_option (line, "--dump-atr"))
+ with_atr = 2;
+ else
+ with_atr = has_option (line, "--atr");
+ handle_more = has_option (line, "--more");
+
+ if ((s=has_option_name (line, "--exlen")))
+ {
+ if (*s == '=')
+ exlen = strtoul (s+1, NULL, 0);
+ else
+ exlen = 4096;
+ }
+ else
+ exlen = 0;
+
+ line = skip_options (line);
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ app = ctrl->app_ctx;
+ if (!app)
+ return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+
+ if (with_atr)
+ {
+ unsigned char *atr;
+ size_t atrlen;
+ char hexbuf[400];
+
+ atr = apdu_get_atr (app->slot, &atrlen);
+ if (!atr || atrlen > sizeof hexbuf - 2 )
+ {
+ rc = gpg_error (GPG_ERR_INV_CARD);
+ goto leave;
+ }
+ if (with_atr == 2)
+ {
+ char *string, *p, *pend;
+
+ string = atr_dump (atr, atrlen);
+ if (string)
+ {
+ for (rc=0, p=string; !rc && (pend = strchr (p, '\n')); p = pend+1)
+ {
+ rc = assuan_send_data (ctx, p, pend - p + 1);
+ if (!rc)
+ rc = assuan_send_data (ctx, NULL, 0);
+ }
+ if (!rc && *p)
+ rc = assuan_send_data (ctx, p, strlen (p));
+ es_free (string);
+ if (rc)
+ goto leave;
+ }
+ }
+ else
+ {
+ bin2hex (atr, atrlen, hexbuf);
+ send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0);
+ }
+ xfree (atr);
+ }
+
+ apdu = hex_to_buffer (line, &apdulen);
+ if (!apdu)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (apdulen)
+ {
+ unsigned char *result = NULL;
+ size_t resultlen;
+
+ rc = apdu_send_direct (app->slot, exlen,
+ apdu, apdulen, handle_more,
+ NULL, &result, &resultlen);
+ if (rc)
+ log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc));
+ else
+ {
+ rc = assuan_send_data (ctx, result, resultlen);
+ xfree (result);
+ }
+ }
+ xfree (apdu);
+
+ leave:
+ return rc;
+}
+
+
+static const char hlp_killscd[] =
+ "KILLSCD\n"
+ "\n"
+ "Commit suicide.";
+static gpg_error_t
+cmd_killscd (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+
+ (void)line;
+
+ ctrl->server_local->stopme = 1;
+ assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+ return 0;
+}
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (assuan_context_t ctx)
+{
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ { "SERIALNO", cmd_serialno, hlp_serialno },
+ { "LEARN", cmd_learn, hlp_learn },
+ { "READCERT", cmd_readcert, hlp_readcert },
+ { "READKEY", cmd_readkey, hlp_readkey },
+ { "SETDATA", cmd_setdata, hlp_setdata },
+ { "PKSIGN", cmd_pksign, hlp_pksign },
+ { "PKAUTH", cmd_pkauth, hlp_pkauth },
+ { "PKDECRYPT", cmd_pkdecrypt,hlp_pkdecrypt },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
+ { "GETATTR", cmd_getattr, hlp_getattr },
+ { "SETATTR", cmd_setattr, hlp_setattr },
+ { "WRITECERT", cmd_writecert,hlp_writecert },
+ { "WRITEKEY", cmd_writekey, hlp_writekey },
+ { "GENKEY", cmd_genkey, hlp_genkey },
+ { "RANDOM", cmd_random, hlp_random },
+ { "PASSWD", cmd_passwd, hlp_passwd },
+ { "CHECKPIN", cmd_checkpin, hlp_checkpin },
+ { "LOCK", cmd_lock, hlp_lock },
+ { "UNLOCK", cmd_unlock, hlp_unlock },
+ { "GETINFO", cmd_getinfo, hlp_getinfo },
+ { "RESTART", cmd_restart, hlp_restart },
+ { "DISCONNECT", cmd_disconnect,hlp_disconnect },
+ { "APDU", cmd_apdu, hlp_apdu },
+ { "KILLSCD", cmd_killscd, hlp_killscd },
+ { NULL }
+ };
+ int i, rc;
+
+ for (i=0; table[i].name; i++)
+ {
+ rc = assuan_register_command (ctx, table[i].name, table[i].handler,
+ table[i].help);
+ if (rc)
+ return rc;
+ }
+ assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready");
+
+ assuan_register_reset_notify (ctx, reset_notify);
+ assuan_register_option_handler (ctx, option_handler);
+ return 0;
+}
+
+
+/* Startup the server. If FD is given as -1 this is simple pipe
+ server, otherwise it is a regular server. Returns true if there
+ are no more active asessions. */
+int
+scd_command_handler (ctrl_t ctrl, int fd)
+{
+ int rc;
+ assuan_context_t ctx = NULL;
+ int stopme;
+
+ rc = assuan_new (&ctx);
+ if (rc)
+ {
+ log_error ("failed to allocate assuan context: %s\n",
+ gpg_strerror (rc));
+ scd_exit (2);
+ }
+
+ if (fd == -1)
+ {
+ assuan_fd_t filedes[2];
+
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+ rc = assuan_init_pipe_server (ctx, filedes);
+ }
+ else
+ {
+ rc = assuan_init_socket_server (ctx, INT2FD(fd),
+ ASSUAN_SOCKET_SERVER_ACCEPTED);
+ }
+ if (rc)
+ {
+ log_error ("failed to initialize the server: %s\n",
+ gpg_strerror(rc));
+ scd_exit (2);
+ }
+ rc = register_commands (ctx);
+ if (rc)
+ {
+ log_error ("failed to register commands with Assuan: %s\n",
+ gpg_strerror(rc));
+ scd_exit (2);
+ }
+ assuan_set_pointer (ctx, ctrl);
+
+ /* Allocate and initialize the server object. Put it into the list
+ of active sessions. */
+ ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
+ ctrl->server_local->next_session = session_list;
+ session_list = ctrl->server_local;
+ ctrl->server_local->ctrl_backlink = ctrl;
+ ctrl->server_local->assuan_ctx = ctx;
+
+ /* Command processing loop. */
+ for (;;)
+ {
+ rc = assuan_accept (ctx);
+ if (rc == -1)
+ {
+ break;
+ }
+ else if (rc)
+ {
+ log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
+ break;
+ }
+
+ rc = assuan_process (ctx);
+ if (rc)
+ {
+ log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
+ continue;
+ }
+ }
+
+ /* Cleanup. We don't send an explicit reset to the card. */
+ do_reset (ctrl, 0, 0);
+
+ /* Release the server object. */
+ if (session_list == ctrl->server_local)
+ session_list = ctrl->server_local->next_session;
+ else
+ {
+ struct server_local_s *sl;
+
+ for (sl=session_list; sl->next_session; sl = sl->next_session)
+ if (sl->next_session == ctrl->server_local)
+ break;
+ if (!sl->next_session)
+ BUG ();
+ sl->next_session = ctrl->server_local->next_session;
+ }
+ stopme = ctrl->server_local->stopme;
+ xfree (ctrl->server_local);
+ ctrl->server_local = NULL;
+
+ /* Release the Assuan context. */
+ assuan_release (ctx);
+
+ if (stopme)
+ scd_exit (0);
+
+ /* If there are no more sessions return true. */
+ return !session_list;
+}
+
+
+
+/* Send a keyinfo string. If DATA is true the string is emitted as a
+ * data line, else as a status line. */
+void
+send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
+ const char *serialno, const char *idstr)
+{
+ char *string;
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ string = xtryasprintf ("%s T %s %s%s", keygrip_str,
+ serialno? serialno : "-",
+ idstr? idstr : "-",
+ data? "\n" : "");
+
+ if (!string)
+ return;
+
+ if (!data)
+ assuan_write_status (ctx, "KEYINFO", string);
+ else
+ assuan_send_data (ctx, string, strlen (string));
+
+ xfree (string);
+ return;
+}
+
+
+/* Send a line with status information via assuan and escape all given
+ buffers. The variable elements are pairs of (char *, size_t),
+ terminated with a (NULL, 0). */
+void
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ const unsigned char *value;
+ size_t valuelen;
+ char buf[950], *p;
+ size_t n;
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ while ( (value = va_arg (arg_ptr, const unsigned char *))
+ && n < DIM (buf)-2 )
+ {
+ valuelen = va_arg (arg_ptr, size_t);
+ if (!valuelen)
+ continue; /* empty buffer */
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+ {
+ if (*value == '+' || *value == '\"' || *value == '%'
+ || *value < ' ')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ n += 2;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ }
+ *p = 0;
+ assuan_write_status (ctx, keyword, buf);
+
+ va_end (arg_ptr);
+}
+
+
+/* Send a ready formatted status line via assuan. */
+gpg_error_t
+send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
+{
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ if (strchr (args, '\n'))
+ {
+ log_error ("error: LF detected in status line - not sending\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+ return assuan_write_status (ctx, keyword, args);
+}
+
+
+/* This status functions expects a printf style format string. No
+ * filtering of the data is done instead the orintf formatted data is
+ * send using assuan_send_status. */
+gpg_error_t
+send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...)
+{
+ gpg_error_t err;
+ va_list arg_ptr;
+ assuan_context_t ctx;
+
+ if (!ctrl || !ctrl->server_local || !(ctx = ctrl->server_local->assuan_ctx))
+ return 0;
+
+ va_start (arg_ptr, format);
+ err = vprint_assuan_status (ctx, keyword, format, arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}
+
+
+void
+popup_prompt (void *opaque, int on)
+{
+ ctrl_t ctrl = opaque;
+
+ if (ctrl)
+ {
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ if (ctx)
+ {
+ const char *cmd;
+ gpg_error_t err;
+ unsigned char *value;
+ size_t valuelen;
+
+ if (on)
+ cmd = "POPUPPINPADPROMPT --ack";
+ else
+ cmd = "DISMISSPINPADPROMPT";
+ err = assuan_inquire (ctx, cmd, &value, &valuelen, 100);
+ if (!err)
+ xfree (value);
+ }
+ }
+}
+
+
+/* Helper to send the clients a status change notification. */
+void
+send_client_notifications (app_t app, int removal)
+{
+ struct {
+ pid_t pid;
+#ifdef HAVE_W32_SYSTEM
+ HANDLE handle;
+#else
+ int signo;
+#endif
+ } killed[50];
+ int killidx = 0;
+ int kidx;
+ struct server_local_s *sl;
+
+ for (sl=session_list; sl; sl = sl->next_session)
+ if (sl->ctrl_backlink && sl->ctrl_backlink->app_ctx == app)
+ {
+ pid_t pid;
+#ifdef HAVE_W32_SYSTEM
+ HANDLE handle;
+#else
+ int signo;
+#endif
+
+ if (removal)
+ {
+ sl->ctrl_backlink->app_ctx = NULL;
+ sl->card_removed = 1;
+ release_application (app, 1);
+ }
+
+ if (!sl->event_signal || !sl->assuan_ctx)
+ continue;
+
+ pid = assuan_get_pid (sl->assuan_ctx);
+
+#ifdef HAVE_W32_SYSTEM
+ handle = sl->event_signal;
+ for (kidx=0; kidx < killidx; kidx++)
+ if (killed[kidx].pid == pid
+ && killed[kidx].handle == handle)
+ break;
+ if (kidx < killidx)
+ log_info ("event %p (%p) already triggered for client %d\n",
+ sl->event_signal, handle, (int)pid);
+ else
+ {
+ log_info ("triggering event %p (%p) for client %d\n",
+ sl->event_signal, handle, (int)pid);
+ if (!SetEvent (handle))
+ log_error ("SetEvent(%p) failed: %s\n",
+ sl->event_signal, w32_strerror (-1));
+ if (killidx < DIM (killed))
+ {
+ killed[killidx].pid = pid;
+ killed[killidx].handle = handle;
+ killidx++;
+ }
+ }
+#else /*!HAVE_W32_SYSTEM*/
+ signo = sl->event_signal;
+
+ if (pid != (pid_t)(-1) && pid && signo > 0)
+ {
+ for (kidx=0; kidx < killidx; kidx++)
+ if (killed[kidx].pid == pid
+ && killed[kidx].signo == signo)
+ break;
+ if (kidx < killidx)
+ log_info ("signal %d already sent to client %d\n",
+ signo, (int)pid);
+ else
+ {
+ log_info ("sending signal %d to client %d\n",
+ signo, (int)pid);
+ kill (pid, signo);
+ if (killidx < DIM (killed))
+ {
+ killed[killidx].pid = pid;
+ killed[killidx].signo = signo;
+ killidx++;
+ }
+ }
+ }
+#endif /*!HAVE_W32_SYSTEM*/
+ }
+}
diff --git a/scd/iso7816.c b/scd/iso7816.c
new file mode 100644
index 0000000..c878a03
--- /dev/null
+++ b/scd/iso7816.c
@@ -0,0 +1,1034 @@
+/* iso7816.c - ISO 7816 commands
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
+/* This is used with GnuPG version < 1.9. The code has been source
+ copied from the current GnuPG >= 1.9 and is maintained over
+ there. */
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
+#else /* GNUPG_MAJOR_VERSION != 1 */
+#include "scdaemon.h"
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+
+#include "iso7816.h"
+#include "apdu.h"
+
+
+#define CMD_SELECT_FILE 0xA4
+#define CMD_VERIFY ISO7816_VERIFY
+#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
+#define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER
+#define CMD_GET_DATA 0xCA
+#define CMD_PUT_DATA 0xDA
+#define CMD_MSE 0x22
+#define CMD_PSO 0x2A
+#define CMD_GENERAL_AUTHENTICATE 0x87
+#define CMD_INTERNAL_AUTHENTICATE 0x88
+#define CMD_GENERATE_KEYPAIR 0x47
+#define CMD_GET_CHALLENGE 0x84
+#define CMD_READ_BINARY 0xB0
+#define CMD_READ_RECORD 0xB2
+
+static gpg_error_t
+map_sw (int sw)
+{
+ gpg_err_code_t ec;
+
+ switch (sw)
+ {
+ case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+ case SW_TERM_STATE: ec = GPG_ERR_OBJ_TERM_STATE; break;
+ case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
+ case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_FILE_STRUCT: ec = GPG_ERR_CARD; break;
+ case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
+ case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
+ case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
+ case SW_NO_CURRENT_EF: ec = GPG_ERR_ENOENT; break;
+ case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
+ case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
+ case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
+ case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
+ case SW_INCORRECT_P0_P1:ec = GPG_ERR_INV_VALUE; break;
+ case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
+ case SW_EXACT_LENGTH: ec = GPG_ERR_INV_VALUE; break;
+ case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
+ case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break;
+ case SW_SUCCESS: ec = 0; break;
+
+ case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
+ case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
+ case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
+ case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
+ case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
+ case SW_HOST_NO_CARD: ec = GPG_ERR_CARD_NOT_PRESENT; break;
+ case SW_HOST_CARD_INACTIVE: ec = GPG_ERR_CARD_RESET; break;
+ case SW_HOST_CARD_IO_ERROR: ec = GPG_ERR_EIO; break;
+ case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break;
+ case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break;
+ case SW_HOST_ABORTED: ec = GPG_ERR_INV_RESPONSE; break;
+ case SW_HOST_NO_PINPAD: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_HOST_CANCELLED: ec = GPG_ERR_CANCELED; break;
+ case SW_HOST_USB_OTHER: ec = GPG_ERR_EIO; break;
+ case SW_HOST_USB_IO: ec = GPG_ERR_EIO; break;
+ case SW_HOST_USB_ACCESS: ec = GPG_ERR_EACCES; break;
+ case SW_HOST_USB_NO_DEVICE: ec = GPG_ERR_ENODEV; break;
+ case SW_HOST_USB_BUSY: ec = GPG_ERR_EBUSY; break;
+ case SW_HOST_USB_TIMEOUT: ec = GPG_ERR_TIMEOUT; break;
+ case SW_HOST_USB_OVERFLOW: ec = GPG_ERR_EOVERFLOW; break;
+
+ default:
+ if ((sw & 0x010000))
+ ec = GPG_ERR_GENERAL; /* Should not happen. */
+ else if ((sw & 0xff00) == SW_MORE_DATA)
+ ec = 0; /* This should actually never been seen here. */
+ else if ((sw & 0xfff0) == 0x63C0)
+ ec = GPG_ERR_BAD_PIN;
+ else
+ ec = GPG_ERR_CARD;
+ }
+ return gpg_error (ec);
+}
+
+/* Map a status word from the APDU layer to a gpg-error code. */
+gpg_error_t
+iso7816_map_sw (int sw)
+{
+ /* All APDU functions should return 0x9000 on success but for
+ historical reasons of the implementation some return 0 to
+ indicate success. We allow for that here. */
+ return sw? map_sw (sw) : 0;
+}
+
+
+/* This function is specialized version of the SELECT FILE command.
+ SLOT is the card and reader as created for example by
+ apdu_open_reader (), AID is a buffer of size AIDLEN holding the
+ requested application ID. The function can't be used to enumerate
+ AIDs and won't return the AID on success. The return value is 0
+ for okay or a GPG error code. Note that ISO error codes are
+ internally mapped. Bit 0 of FLAGS should be set if the card does
+ not understand P2=0xC0. */
+gpg_error_t
+iso7816_select_application (int slot, const char *aid, size_t aidlen,
+ unsigned int flags)
+{
+ int sw;
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
+ (flags&1)? 0 :0x0c, aidlen, aid);
+ return map_sw (sw);
+}
+
+
+/* This is the same as iso7816_select_application but may return data
+ * at RESULT,RESULTLEN). */
+gpg_error_t
+iso7816_select_application_ext (int slot, const char *aid, size_t aidlen,
+ unsigned int flags,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ sw = apdu_send (slot, 0, 0x00, CMD_SELECT_FILE, 4,
+ (flags&1)? 0:0x0c, aidlen, aid,
+ result, resultlen);
+ return map_sw (sw);
+}
+
+
+/* Simple MF selection as supported by some cards. */
+gpg_error_t
+iso7816_select_mf (int slot)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 0x000, 0x0c, -1, NULL);
+ return map_sw (sw);
+}
+
+
+gpg_error_t
+iso7816_select_file (int slot, int tag, int is_dir)
+{
+ int sw, p0, p1;
+ unsigned char tagbuf[2];
+
+ tagbuf[0] = (tag >> 8) & 0xff;
+ tagbuf[1] = tag & 0xff;
+
+ p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
+ p1 = 0x0c; /* No FC return. */
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
+ p0, p1, 2, (char*)tagbuf );
+ return map_sw (sw);
+}
+
+
+/* Do a select file command with a direct path. If TOPDF is set, the
+ * actual used path is 3f00/<topdf>/<path>. */
+gpg_error_t
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
+ unsigned short topdf)
+{
+ int sw, p0, p1;
+ unsigned char buffer[100];
+ int buflen = 0;
+
+ if (pathlen*2 + 2 >= sizeof buffer)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ if (topdf)
+ {
+ buffer[buflen++] = topdf >> 8;
+ buffer[buflen++] = topdf;
+ }
+
+ for (; pathlen; pathlen--, path++)
+ {
+ buffer[buflen++] = (*path >> 8);
+ buffer[buflen++] = *path;
+ }
+
+ p0 = 0x08;
+ p1 = 0x0c; /* No FC return. */
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
+ p0, p1, buflen, (char*)buffer );
+ return map_sw (sw);
+}
+
+
+/* This is a private command currently only working for TCOS cards. */
+gpg_error_t
+iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ return map_sw (sw);
+}
+
+
+/* This function sends an already formatted APDU to the card. With
+ HANDLE_MORE set to true a MORE DATA status will be handled
+ internally. The return value is a gpg error code (i.e. a mapped
+ status word). This is basically the same as apdu_send_direct but
+ it maps the status word and does not return it in the result
+ buffer. However, it R_SW is not NULL the status word is stored
+ R_SW for closer inspection. */
+gpg_error_t
+iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
+ int handle_more, unsigned int *r_sw,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw, sw2;
+
+ if (result)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ }
+
+ sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
+ &sw2, result, resultlen);
+ if (!sw)
+ {
+ if (!result)
+ sw = sw2;
+ else if (*resultlen < 2)
+ sw = SW_HOST_GENERAL_ERROR;
+ else
+ {
+ sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1];
+ (*resultlen)--;
+ (*resultlen)--;
+ }
+ }
+ if (sw != SW_SUCCESS && result)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ if (r_sw)
+ *r_sw = sw;
+ return map_sw (sw);
+}
+
+
+/* Check whether the reader supports the ISO command code COMMAND on
+ the pinpad. Returns 0 on success. */
+gpg_error_t
+iso7816_check_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+ int sw;
+
+ sw = apdu_check_pinpad (slot, command, pininfo);
+ return iso7816_map_sw (sw);
+}
+
+
+/* Perform a VERIFY command on SLOT using the card holder verification
+ vector CHVNO. With PININFO non-NULL the pinpad of the reader will
+ be used. Returns 0 on success. */
+gpg_error_t
+iso7816_verify_kp (int slot, int chvno, pininfo_t *pininfo)
+{
+ int sw;
+
+ sw = apdu_pinpad_verify (slot, 0x00, CMD_VERIFY, 0, chvno, pininfo);
+ return map_sw (sw);
+}
+
+/* Perform a VERIFY command on SLOT using the card holder verification
+ vector CHVNO with a CHV of length CHVLEN. Returns 0 on success. */
+gpg_error_t
+iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ return map_sw (sw);
+}
+
+
+/* Some cards support a VERIFY command variant to check the status of
+ * the the CHV without a need to try a CHV. In contrast to the other
+ * functions this function returns the special codes ISO7816_VERIFY_*
+ * or a non-negative number with the left attempts. */
+int
+iso7816_verify_status (int slot, int chvno)
+{
+ unsigned char apdu[4];
+ unsigned int sw;
+ int result;
+
+ apdu[0] = 0x00;
+ apdu[1] = ISO7816_VERIFY;
+ apdu[2] = 0x00;
+ apdu[3] = chvno;
+ if (!iso7816_apdu_direct (slot, apdu, 4, 0, &sw, NULL, NULL))
+ result = ISO7816_VERIFY_NOT_NEEDED; /* Not returned by all cards. */
+ else if (sw == 0x6a88 || sw == 0x6a80)
+ result = ISO7816_VERIFY_NO_PIN;
+ else if (sw == 0x6983)
+ result = ISO7816_VERIFY_BLOCKED;
+ else if (sw == 0x6985)
+ result = ISO7816_VERIFY_NULLPIN; /* TCOS card */
+ else if ((sw & 0xfff0) == 0x63C0)
+ result = (sw & 0x000f);
+ else
+ result = ISO7816_VERIFY_ERROR;
+
+ return result;
+}
+
+
+/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+ verification vector CHVNO. With PININFO non-NULL the pinpad of the
+ reader will be used. If IS_EXCHANGE is 0, a "change reference
+ data" is done, otherwise an "exchange reference data". */
+gpg_error_t
+iso7816_change_reference_data_kp (int slot, int chvno, int is_exchange,
+ pininfo_t *pininfo)
+{
+ int sw;
+
+ sw = apdu_pinpad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ is_exchange ? 1 : 0, chvno, pininfo);
+ return map_sw (sw);
+}
+
+/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+ verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
+ 0), a "change reference data" is done, otherwise an "exchange
+ reference data". The new reference data is expected in NEWCHV of
+ length NEWCHVLEN. */
+gpg_error_t
+iso7816_change_reference_data (int slot, int chvno,
+ const char *oldchv, size_t oldchvlen,
+ const char *newchv, size_t newchvlen)
+{
+ int sw;
+ char *buf;
+
+ if ((!oldchv && oldchvlen)
+ || (oldchv && !oldchvlen)
+ || !newchv || !newchvlen )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ buf = xtrymalloc (oldchvlen + newchvlen);
+ if (!buf)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ if (oldchvlen)
+ memcpy (buf, oldchv, oldchvlen);
+ memcpy (buf+oldchvlen, newchv, newchvlen);
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+ wipememory (buf, oldchvlen+newchvlen);
+ xfree (buf);
+ return map_sw (sw);
+
+}
+
+
+gpg_error_t
+iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+ const char *data, size_t datalen)
+{
+ int sw;
+
+ if (!data || !datalen )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+ 0, chvno, datalen, data);
+ return map_sw (sw);
+}
+
+
+gpg_error_t
+iso7816_reset_retry_counter (int slot, int chvno,
+ const char *newchv, size_t newchvlen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+ 2, chvno, newchvlen, newchv);
+ return map_sw (sw);
+}
+
+
+
+/* Perform a GET DATA command requesting TAG and storing the result in
+ a newly allocated buffer at the address passed by RESULT. Return
+ the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_get_data (int slot, int extended_mode, int tag,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ int le;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (extended_mode > 0 && extended_mode < 256)
+ le = 65534; /* Not 65535 in case it is used as some special flag. */
+ else if (extended_mode > 0)
+ le = extended_mode;
+ else
+ le = 256;
+
+ sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA,
+ ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Perform a PUT DATA command on card in SLOT. Write DATA of length
+ DATALEN to TAG. EXTENDED_MODE controls whether extended length
+ headers or command chaining is used instead of single length
+ bytes. */
+gpg_error_t
+iso7816_put_data (int slot, int extended_mode, int tag,
+ const void *data, size_t datalen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA,
+ ((tag >> 8) & 0xff), (tag & 0xff),
+ datalen, (const char*)data);
+ return map_sw (sw);
+}
+
+/* Same as iso7816_put_data but uses an odd instruction byte. */
+gpg_error_t
+iso7816_put_data_odd (int slot, int extended_mode, int tag,
+ const void *data, size_t datalen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1,
+ ((tag >> 8) & 0xff), (tag & 0xff),
+ datalen, (const char*)data);
+ return map_sw (sw);
+}
+
+/* Manage Security Environment. This is a weird operation and there
+ is no easy abstraction for it. Furthermore, some card seem to have
+ a different interpreation of 7816-8 and thus we resort to let the
+ caller decide what to do. */
+gpg_error_t
+iso7816_manage_security_env (int slot, int p1, int p2,
+ const unsigned char *data, size_t datalen)
+{
+ int sw;
+
+ if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2,
+ data? datalen : -1, (const char*)data);
+ return map_sw (sw);
+}
+
+
+/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
+ success 0 is returned and the data is availavle in a newly
+ allocated buffer stored at RESULT with its length stored at
+ RESULTLEN. For LE see do_generate_keypair. */
+gpg_error_t
+iso7816_compute_ds (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_PSO, 0x9E, 0x9A,
+ datalen, (const char*)data,
+ le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Perform the security operation DECIPHER. PADIND is the padding
+ indicator to be used. It should be 0 if no padding is required, a
+ value of -1 suppresses the padding byte. On success 0 is returned
+ and the plaintext is available in a newly allocated buffer stored
+ at RESULT with its length stored at RESULTLEN. For LE see
+ do_generate_keypair. */
+gpg_error_t
+iso7816_decipher (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ int padind, unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buf;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ if (padind >= 0)
+ {
+ /* We need to prepend the padding indicator. */
+ buf = xtrymalloc (datalen + 1);
+ if (!buf)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ *buf = padind; /* Padding indicator. */
+ memcpy (buf+1, data, datalen);
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_PSO, 0x80, 0x86,
+ datalen+1, (char*)buf, le,
+ result, resultlen);
+ xfree (buf);
+ }
+ else
+ {
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_PSO, 0x80, 0x86,
+ datalen, (const char *)data, le,
+ result, resultlen);
+ }
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Perform the security operation COMPUTE SHARED SECRET. On success 0
+ is returned and the shared secret is available in a newly allocated
+ buffer stored at RESULT with its length stored at RESULTLEN. For
+ LE see do_generate_keypair. */
+gpg_error_t
+iso7816_pso_csv (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buf;
+ unsigned int nbuf;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ /* Data needs to be in BER-TLV format. */
+ buf = xtrymalloc (datalen + 4);
+ if (!buf)
+ return gpg_error_from_syserror ();
+ nbuf = 0;
+ buf[nbuf++] = 0x9c;
+ if (datalen < 128)
+ buf[nbuf++] = datalen;
+ else if (datalen < 256)
+ {
+ buf[nbuf++] = 0x81;
+ buf[nbuf++] = datalen;
+ }
+ else
+ {
+ buf[nbuf++] = 0x82;
+ buf[nbuf++] = datalen << 8;
+ buf[nbuf++] = datalen;
+ }
+ memcpy (buf+nbuf, data, datalen);
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_PSO, 0x80, 0xa6,
+ datalen+nbuf, (const char *)buf, le,
+ result, resultlen);
+ xfree (buf);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* For LE see do_generate_keypair. */
+gpg_error_t
+iso7816_internal_authenticate (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
+ datalen, (const char*)data,
+ le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* For LE see do_generate_keypair. */
+gpg_error_t
+iso7816_general_authenticate (int slot, int extended_mode,
+ int algoref, int keyref,
+ const unsigned char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!extended_mode)
+ le = 256; /* Ignore provided Le and use what apdu_send uses. */
+ else if (le >= 0 && le < 256)
+ le = 256;
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_GENERAL_AUTHENTICATE, algoref, keyref,
+ datalen, (const char*)data,
+ le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* LE is the expected return length. This is usually 0 except if
+ extended length mode is used and more than 256 byte will be
+ returned. In that case a value of -1 uses a large default
+ (e.g. 4096 bytes), a value larger 256 used that value. */
+static gpg_error_t
+do_generate_keypair (int slot, int extended_mode, int p1, int p2,
+ const char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send_le (slot, extended_mode,
+ 0x00, CMD_GENERATE_KEYPAIR, p1, p2,
+ datalen, data,
+ le >= 0 && le < 256? 256:le,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+gpg_error_t
+iso7816_generate_keypair (int slot, int extended_mode, int p1, int p2,
+ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+{
+ return do_generate_keypair (slot, extended_mode, p1, p2,
+ data, datalen, le, result, resultlen);
+}
+
+
+gpg_error_t
+iso7816_read_public_key (int slot, int extended_mode,
+ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen)
+{
+ return do_generate_keypair (slot, extended_mode, 0x81, 0,
+ data, datalen, le, result, resultlen);
+}
+
+
+
+gpg_error_t
+iso7816_get_challenge (int slot, int length, unsigned char *buffer)
+{
+ int sw;
+ unsigned char *result;
+ size_t resultlen, n;
+
+ if (!buffer || length < 1)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ do
+ {
+ result = NULL;
+ n = length > 254? 254 : length;
+ sw = apdu_send_le (slot, 0,
+ 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, n,
+ &result, &resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (result);
+ return map_sw (sw);
+ }
+ if (resultlen > n)
+ resultlen = n;
+ memcpy (buffer, result, resultlen);
+ buffer += resultlen;
+ length -= resultlen;
+ xfree (result);
+ }
+ while (length > 0);
+
+ return 0;
+}
+
+/* Perform a READ BINARY command requesting a maximum of NMAX bytes
+ * from OFFSET. With NMAX = 0 the entire file is read. The result is
+ * stored in a newly allocated buffer at the address passed by RESULT.
+ * Returns the length of this data at the address of RESULTLEN. If
+ * R_SW is not NULL the last status word is stored there. */
+gpg_error_t
+iso7816_read_binary_ext (int slot, int extended_mode,
+ size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen,
+ int *r_sw)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+ int read_all = !nmax;
+ size_t n;
+
+ if (r_sw)
+ *r_sw = 0;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (offset > 32767)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ do
+ {
+ buffer = NULL;
+ bufferlen = 0;
+ n = read_all? 0 : nmax;
+ sw = apdu_send_le (slot, extended_mode, 0x00, CMD_READ_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+ n, &buffer, &bufferlen);
+ if ( SW_EXACT_LENGTH_P(sw) )
+ {
+ n = (sw & 0x00ff);
+ sw = apdu_send_le (slot, extended_mode, 0x00, CMD_READ_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+ n, &buffer, &bufferlen);
+ }
+ if (r_sw)
+ *r_sw = sw;
+
+ if (*result && sw == SW_BAD_P0_P1)
+ {
+ /* Bad Parameter means that the offset is outside of the
+ EF. When reading all data we take this as an indication
+ for EOF. */
+ break;
+ }
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ if (*result) /* Need to extend the buffer. */
+ {
+ unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
+ if (!p)
+ {
+ gpg_error_t err = gpg_error_from_syserror ();
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return err;
+ }
+ *result = p;
+ memcpy (*result + *resultlen, buffer, bufferlen);
+ *resultlen += bufferlen;
+ xfree (buffer);
+ buffer = NULL;
+ }
+ else /* Transfer the buffer into our result. */
+ {
+ *result = buffer;
+ *resultlen = bufferlen;
+ }
+ offset += bufferlen;
+ if (offset > 32767)
+ break; /* We simply truncate the result for too large
+ files. */
+ if (nmax > bufferlen)
+ nmax -= bufferlen;
+ else
+ nmax = 0;
+ }
+ while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
+
+ return 0;
+}
+
+
+gpg_error_t
+iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen)
+{
+ return iso7816_read_binary_ext (slot, 0, offset, nmax,
+ result, resultlen, NULL);
+}
+
+
+/* Perform a READ RECORD command. RECNO gives the record number to
+ read with 0 indicating the current record. RECCOUNT must be 1 (not
+ all cards support reading of more than one record). SHORT_EF
+ should be 0 to read the current EF or contain a short EF. The
+ result is stored in a newly allocated buffer at the address passed
+ by RESULT. Returns the length of this data at the address of
+ RESULTLEN. If R_SW is not NULL the last status word is stored
+ there. */
+gpg_error_t
+iso7816_read_record_ext (int slot, int recno, int reccount, int short_ef,
+ unsigned char **result, size_t *resultlen,
+ int *r_sw)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+
+ if (r_sw)
+ *r_sw = 0;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (recno < 0 || recno > 255 || reccount != 1
+ || short_ef < 0 || short_ef > 254 )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ buffer = NULL;
+ bufferlen = 0;
+ sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD,
+ recno,
+ short_ef? short_ef : 0x04,
+ -1, NULL,
+ 0, &buffer, &bufferlen);
+ if (r_sw)
+ *r_sw = sw;
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ *result = buffer;
+ *resultlen = bufferlen;
+
+ return 0;
+}
+
+gpg_error_t
+iso7816_read_record (int slot, int recno, int reccount, int short_ef,
+ unsigned char **result, size_t *resultlen)
+{
+ return iso7816_read_record_ext (slot, recno, reccount, short_ef,
+ result, resultlen, NULL);
+}
diff --git a/scd/iso7816.h b/scd/iso7816.h
new file mode 100644
index 0000000..d22f0be
--- /dev/null
+++ b/scd/iso7816.h
@@ -0,0 +1,154 @@
+/* iso7816.h - ISO 7816 commands
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ISO7816_H
+#define ISO7816_H
+
+#if GNUPG_MAJOR_VERSION == 1
+#include "cardglue.h"
+#endif
+
+/* Command codes used by iso7816_check_pinpad. */
+#define ISO7816_VERIFY 0x20
+#define ISO7816_CHANGE_REFERENCE_DATA 0x24
+#define ISO7816_RESET_RETRY_COUNTER 0x2C
+
+/* Error codes returned by iso7816_verify_status. A non-negative
+ * number gives the number of left tries.
+ * NB: The values are also used by the CHV-STATUS lines and thus are
+ * part of the public interface. Do not change them. */
+#define ISO7816_VERIFY_ERROR (-1)
+#define ISO7816_VERIFY_NO_PIN (-2)
+#define ISO7816_VERIFY_BLOCKED (-3)
+#define ISO7816_VERIFY_NULLPIN (-4)
+#define ISO7816_VERIFY_NOT_NEEDED (-5)
+
+/* Information to be passed to pinpad equipped readers. See
+ ccid-driver.c for details. */
+struct pininfo_s
+{
+ int fixedlen; /*
+ * -1: Variable length input is not supported,
+ * no information of fixed length yet.
+ * 0: Use variable length input.
+ * >0: Fixed length of PIN.
+ */
+ int minlen;
+ int maxlen;
+};
+typedef struct pininfo_s pininfo_t;
+
+
+gpg_error_t iso7816_map_sw (int sw);
+
+gpg_error_t iso7816_select_application (int slot,
+ const char *aid, size_t aidlen,
+ unsigned int flags);
+gpg_error_t iso7816_select_application_ext (int slot,
+ const char *aid, size_t aidlen,
+ unsigned int flags,
+ unsigned char **result,
+ size_t *resultlen);
+gpg_error_t iso7816_select_mf (int slot);
+gpg_error_t iso7816_select_file (int slot, int tag, int is_dir);
+gpg_error_t iso7816_select_path (int slot,
+ const unsigned short *path, size_t pathlen,
+ unsigned short top_df);
+gpg_error_t iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_apdu_direct (int slot,
+ const void *apdudata, size_t apdudatalen,
+ int handle_more, unsigned int *r_sw,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_check_pinpad (int slot, int command,
+ pininfo_t *pininfo);
+gpg_error_t iso7816_verify (int slot,
+ int chvno, const char *chv, size_t chvlen);
+gpg_error_t iso7816_verify_kp (int slot, int chvno, pininfo_t *pininfo);
+int iso7816_verify_status (int slot, int chvno);
+gpg_error_t iso7816_change_reference_data (int slot, int chvno,
+ const char *oldchv, size_t oldchvlen,
+ const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno,
+ int is_exchange,
+ pininfo_t *pininfo);
+gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
+ const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+ const char *data,
+ size_t datalen);
+gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
+ const void *data, size_t datalen);
+gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag,
+ const void *data, size_t datalen);
+gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
+ const unsigned char *data,
+ size_t datalen);
+gpg_error_t iso7816_compute_ds (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_decipher (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen,
+ int le, int padind,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_pso_csv (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen, int le,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode,
+ const unsigned char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_general_authenticate (int slot, int extended_mode,
+ int algoref, int keyref,
+ const unsigned char *data,
+ size_t datalen,
+ int le,
+ unsigned char **result,
+ size_t *resultlen);
+gpg_error_t iso7816_generate_keypair (int slot, int extended_mode,
+ int p1, int p2,
+ const char *data, size_t datalen,
+ int le,
+ unsigned char **result,
+ size_t *resultlen);
+gpg_error_t iso7816_read_public_key (int slot, int extended_mode,
+ const char *data, size_t datalen,
+ int le,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_get_challenge (int slot,
+ int length, unsigned char *buffer);
+
+gpg_error_t iso7816_read_binary_ext (int slot, int extended_mode,
+ size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen,
+ int *r_sw);
+gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_record_ext (int slot, int recno, int reccount,
+ int short_ef,
+ unsigned char **result, size_t *resultlen,
+ int *r_sw);
+gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
+ int short_ef,
+ unsigned char **result, size_t *resultlen);
+
+#endif /*ISO7816_H*/
diff --git a/scd/scdaemon-w32info.rc b/scd/scdaemon-w32info.rc
new file mode 100644
index 0000000..c1dae54
--- /dev/null
+++ b/scd/scdaemon-w32info.rc
@@ -0,0 +1,52 @@
+/* scdaemon-w32info.rc -*- c -*-
+ * Copyright (C) 2013 g10 Code GmbH
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "afxres.h"
+#include "../common/w32info-rc.h"
+
+1 ICON "../common/gnupg.ico"
+
+1 VERSIONINFO
+ FILEVERSION W32INFO_VI_FILEVERSION
+ PRODUCTVERSION W32INFO_VI_PRODUCTVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/
+#else
+ FILEFLAGS 0x00L
+#endif
+ FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */
+ FILETYPE 0x1L /* VFT_APP (0x1) */
+ FILESUBTYPE 0x0L /* VFT2_UNKNOWN */
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" /* US English (0409), Unicode (04b0) */
+ BEGIN
+ VALUE "FileDescription", L"GnuPG\x2019s smartcard daemon\0"
+ VALUE "InternalName", "scdaemon\0"
+ VALUE "OriginalFilename", "scdaemon.exe\0"
+ VALUE "ProductName", W32INFO_PRODUCTNAME
+ VALUE "ProductVersion", W32INFO_PRODUCTVERSION
+ VALUE "CompanyName", W32INFO_COMPANYNAME
+ VALUE "FileVersion", W32INFO_FILEVERSION
+ VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT
+ VALUE "Comments", W32INFO_COMMENTS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+ END
+
+1 RT_MANIFEST "scdaemon.w32-manifest"
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
new file mode 100644
index 0000000..b62f5b6
--- /dev/null
+++ b/scd/scdaemon.c
@@ -0,0 +1,1454 @@
+/* scdaemon.c - The GnuPG Smartcard Daemon
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2020 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2019 Werner Koch
+ * Copyright (C) 2020 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fcntl.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /*HAVE_W32_SYSTEM*/
+#include <unistd.h>
+#include <signal.h>
+#include <npth.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#define GNUPG_COMMON_NEED_AFLOCAL
+#include "scdaemon.h"
+#include <ksba.h>
+#include <gcrypt.h>
+
+#include <assuan.h> /* malloc hooks */
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "iso7816.h"
+#include "apdu.h"
+#include "ccid-driver.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/asshelp.h"
+#include "../common/exechelp.h"
+#include "../common/init.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
+enum cmd_and_opt_values
+{ aNull = 0,
+ oCsh = 'c',
+ oQuiet = 'q',
+ oSh = 's',
+ oVerbose = 'v',
+
+ oNoVerbose = 500,
+ aGPGConfList,
+ aGPGConfTest,
+ oOptions,
+ oDebug,
+ oDebugAll,
+ oDebugLevel,
+ oDebugWait,
+ oDebugAllowCoreDump,
+ oDebugCCIDDriver,
+ oDebugLogTid,
+ oDebugAssuanLogCats,
+ oNoGreeting,
+ oNoOptions,
+ oHomedir,
+ oNoDetach,
+ oNoGrab,
+ oLogFile,
+ oServer,
+ oMultiServer,
+ oDaemon,
+ oBatch,
+ oReaderPort,
+ oCardTimeout,
+ octapiDriver,
+ opcscDriver,
+ opcscShared,
+ oDisableCCID,
+ oDisableOpenSC,
+ oDisablePinpad,
+ oAllowAdmin,
+ oDenyAdmin,
+ oDisableApplication,
+ oEnablePinpadVarlen,
+ oListenBacklog,
+
+ oNoop
+};
+
+
+
+static ARGPARSE_OPTS opts[] = {
+ ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+ ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+ ARGPARSE_header (NULL, N_("Options used for startup")),
+
+ ARGPARSE_s_n (oServer,"server", N_("run in server mode (foreground)")),
+ ARGPARSE_s_n (oMultiServer, "multi-server",
+ N_("run in multi server mode (foreground)")),
+ ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
+ ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
+ ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")),
+ ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")),
+ ARGPARSE_s_s (oHomedir, "homedir", "@"),
+ ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")),
+ ARGPARSE_noconffile (oNoOptions, "no-options", "@"),
+
+
+ ARGPARSE_header ("Monitor", N_("Options controlling the diagnostic output")),
+
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+ ARGPARSE_s_s (oDebug, "debug", "@"),
+ ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+ ARGPARSE_s_s (oDebugLevel, "debug-level" ,
+ N_("|LEVEL|set the debugging level to LEVEL")),
+ ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+ ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+ ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"),
+ ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
+ ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
+ ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")),
+
+
+ ARGPARSE_header ("Configuration",
+ N_("Options controlling the configuration")),
+
+ ARGPARSE_s_s (oReaderPort, "reader-port",
+ N_("|N|connect to reader at port N")),
+ ARGPARSE_s_s (octapiDriver, "ctapi-driver",
+ N_("|NAME|use NAME as ct-API driver")),
+ ARGPARSE_s_s (opcscDriver, "pcsc-driver",
+ N_("|NAME|use NAME as PC/SC driver")),
+ ARGPARSE_s_n (opcscShared, "pcsc-shared", "@"),
+ ARGPARSE_s_n (oDisableCCID, "disable-ccid",
+#ifdef HAVE_LIBUSB
+ N_("do not use the internal CCID driver")
+#else
+ "@"
+#endif
+ /* end --disable-ccid */),
+ ARGPARSE_s_u (oCardTimeout, "card-timeout",
+ N_("|N|disconnect the card after N seconds of inactivity")),
+
+ ARGPARSE_s_n (oDisablePinpad, "disable-pinpad",
+ N_("do not use a reader's pinpad")),
+ ARGPARSE_ignore (300, "disable-keypad"),
+ ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen",
+ N_("use variable length input for pinpad")),
+ ARGPARSE_s_s (oDisableApplication, "disable-application", "@"),
+ ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
+
+
+ ARGPARSE_header("Security", N_("Options controlling the security")),
+
+ ARGPARSE_s_n (oAllowAdmin, "allow-admin", "@"),
+ ARGPARSE_s_n (oDenyAdmin, "deny-admin",
+ N_("deny the use of admin card commands")),
+
+ /* Stubs for options which are implemented by 2.3 or later. */
+ ARGPARSE_s_s (oNoop, "application-priority", "@"),
+
+ ARGPARSE_end ()
+};
+
+
+/* The list of supported debug flags. */
+static struct debug_flags_s debug_flags [] =
+ {
+ { DBG_MPI_VALUE , "mpi" },
+ { DBG_CRYPTO_VALUE , "crypto" },
+ { DBG_MEMORY_VALUE , "memory" },
+ { DBG_CACHE_VALUE , "cache" },
+ { DBG_MEMSTAT_VALUE, "memstat" },
+ { DBG_HASHING_VALUE, "hashing" },
+ { DBG_IPC_VALUE , "ipc" },
+ { DBG_CARD_VALUE , "card" },
+ { DBG_CARD_IO_VALUE, "cardio" },
+ { DBG_READER_VALUE , "reader" },
+ { 0, NULL }
+ };
+
+
+/* The card driver we use by default for PC/SC. */
+#if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__)
+#define DEFAULT_PCSC_DRIVER "winscard.dll"
+#elif defined(__APPLE__)
+#define DEFAULT_PCSC_DRIVER "/System/Library/Frameworks/PCSC.framework/PCSC"
+#elif defined(__GLIBC__)
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so.1"
+#else
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so"
+#endif
+
+/* The timer tick used to check card removal.
+
+ We poll every 500ms to let the user immediately know a status
+ change.
+
+ For a card reader with an interrupt endpoint, this timer is not
+ used with the internal CCID driver.
+
+ This is not too good for power saving but given that there is no
+ easy way to block on card status changes it is the best we can do.
+ For PC/SC we could in theory use an extra thread to wait for status
+ changes but that requires a native thread because there is no way
+ to make the underlying PC/SC card change function block using a Npth
+ mechanism. Given that a native thread could only be used under W32
+ we don't do that at all. */
+#define TIMERTICK_INTERVAL_SEC (0)
+#define TIMERTICK_INTERVAL_USEC (500000)
+
+/* Flag to indicate that a shutdown was requested. */
+static int shutdown_pending;
+
+/* It is possible that we are currently running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Flag telling whether we are running as a pipe server. */
+static int pipe_server;
+
+/* Name of the communication socket */
+static char *socket_name;
+/* Name of the redirected socket or NULL. */
+static char *redir_socket_name;
+
+/* We need to keep track of the server's nonces (these are dummies for
+ POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+
+/* Value for the listen() backlog argument. Change at runtime with
+ * --listen-backlog. */
+static int listen_backlog = 64;
+
+#ifdef HAVE_W32_SYSTEM
+static HANDLE the_event;
+#else
+/* PID to notify update of usb devices. */
+static pid_t main_thread_pid;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+/* FD to notify changes. */
+static int notify_fd;
+#endif
+
+static char *create_socket_name (char *standard_name);
+static gnupg_fd_t create_server_socket (const char *name,
+ char **r_redir_name,
+ assuan_sock_nonce_t *nonce);
+
+static void *start_connection_thread (void *arg);
+static void handle_connections (int listen_fd);
+
+/* Pth wrapper function definitions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+static int active_connections;
+
+
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+ const char *s;
+ char *result;
+
+ if (maybe_setuid)
+ {
+ gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
+ maybe_setuid = 0;
+ }
+ s = getfnc (NULL);
+ result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
+ strcpy (stpcpy (stpcpy (result, libname), " "), s);
+ return result;
+}
+
+
+static const char *
+my_strusage (int level)
+{
+ static char *ver_gcry, *ver_ksba;
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "GPL-3.0-or-later"; break;
+ case 11: p = "@SCDAEMON@ (@GNUPG@)";
+ break;
+ case 13: p = VERSION; break;
+ case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+ case 20:
+ if (!ver_gcry)
+ ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+ p = ver_gcry;
+ break;
+ case 21:
+ if (!ver_ksba)
+ ver_ksba = make_libversion ("libksba", ksba_check_version);
+ p = ver_ksba;
+ break;
+ case 1:
+ case 40: p = _("Usage: @SCDAEMON@ [options] (-h for help)");
+ break;
+ case 41: p = _("Syntax: scdaemon [options] [command [args]]\n"
+ "Smartcard daemon for @GNUPG@\n");
+ break;
+
+ default: p = NULL;
+ }
+ return p;
+}
+
+
+static int
+tid_log_callback (unsigned long *rvalue)
+{
+ int len = sizeof (*rvalue);
+ npth_t thread;
+
+ thread = npth_self ();
+ if (sizeof (thread) < len)
+ len = sizeof (thread);
+ memcpy (rvalue, &thread, len);
+
+ return 2; /* Use use hex representation. */
+}
+
+
+/* Setup the debugging. With a LEVEL of NULL only the active debug
+ flags are propagated to the subsystems. With LEVEL set, a specific
+ set of debug flags is set; thus overriding all flags already
+ set. */
+static void
+set_debug (const char *level)
+{
+ int numok = (level && digitp (level));
+ int numlvl = numok? atoi (level) : 0;
+
+ if (!level)
+ ;
+ else if (!strcmp (level, "none") || (numok && numlvl < 1))
+ opt.debug = 0;
+ else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
+ opt.debug = DBG_IPC_VALUE;
+ else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
+ opt.debug = DBG_IPC_VALUE;
+ else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
+ opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
+ else if (!strcmp (level, "guru") || numok)
+ {
+ opt.debug = ~0;
+ /* Unless the "guru" string has been used we don't want to allow
+ hashing debugging. The rationale is that people tend to
+ select the highest debug value and would then clutter their
+ disk with debug files which may reveal confidential data. */
+ if (numok)
+ opt.debug &= ~(DBG_HASHING_VALUE);
+ }
+ else
+ {
+ log_error (_("invalid debug-level '%s' given\n"), level);
+ scd_exit(2);
+ }
+
+
+ if (opt.debug && !opt.verbose)
+ opt.verbose = 1;
+ if (opt.debug && opt.quiet)
+ opt.quiet = 0;
+
+ if (opt.debug & DBG_MPI_VALUE)
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+ if (opt.debug & DBG_CRYPTO_VALUE )
+ gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+ if (opt.debug)
+ parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+
+static void
+cleanup (void)
+{
+ if (socket_name && *socket_name)
+ {
+ char *name;
+
+ name = redir_socket_name? redir_socket_name : socket_name;
+
+ gnupg_remove (name);
+ *socket_name = 0;
+ }
+}
+
+static void
+setup_signal_mask (void)
+{
+#ifndef HAVE_W32_SYSTEM
+ npth_sigev_init ();
+ npth_sigev_add (SIGHUP);
+ npth_sigev_add (SIGUSR1);
+ npth_sigev_add (SIGUSR2);
+ npth_sigev_add (SIGINT);
+ npth_sigev_add (SIGCONT);
+ npth_sigev_add (SIGTERM);
+ npth_sigev_fini ();
+ main_thread_pid = getpid ();
+#endif
+}
+
+int
+main (int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ char *last_configname = NULL;
+ const char *configname = NULL;
+ const char *shell;
+ int debug_argparser = 0;
+ const char *debug_level = NULL;
+ int greeting = 0;
+ int nogreeting = 0;
+ int multi_server = 0;
+ int is_daemon = 0;
+ int nodetach = 0;
+ int csh_style = 0;
+ char *logfile = NULL;
+ int debug_wait = 0;
+ int gpgconf_list = 0;
+ char *config_filename = NULL;
+ int allow_coredump = 0;
+ struct assuan_malloc_hooks malloc_hooks;
+ int res;
+ npth_t pipecon_handler;
+
+ early_system_init ();
+ set_strusage (my_strusage);
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ /* Please note that we may running SUID(ROOT), so be very CAREFUL
+ when adding any stuff between here and the call to INIT_SECMEM()
+ somewhere after the option parsing */
+ log_set_prefix ("scdaemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
+
+ /* Make sure that our subsystems are ready. */
+ i18n_init ();
+ init_common_subsystems (&argc, &argv);
+
+ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
+
+ malloc_hooks.malloc = gcry_malloc;
+ malloc_hooks.realloc = gcry_realloc;
+ malloc_hooks.free = gcry_free;
+ assuan_set_malloc_hooks (&malloc_hooks);
+ assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+ assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+ assuan_sock_init ();
+ setup_libassuan_logging (&opt.debug, NULL);
+
+ setup_libgcrypt_logging ();
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+ disable_core_dumps ();
+
+ /* Set default options. */
+ opt.allow_admin = 1;
+ opt.pcsc_driver = DEFAULT_PCSC_DRIVER;
+
+ shell = getenv ("SHELL");
+ if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
+ csh_style = 1;
+
+ /* Check whether we have a config file on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+ while (gnupg_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oDebug:
+ case oDebugAll:
+ debug_argparser++;
+ break;
+ case oHomedir:
+ gnupg_set_homedir (pargs.r.ret_str);
+ break;
+ }
+ }
+ /* Reset the flags. */
+ pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION);
+
+ /* Initialize the secure memory. */
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+ maybe_setuid = 0;
+
+ /*
+ * Now we are working under our real uid
+ */
+
+ /* The configuraton directories for use by gpgrt_argparser. */
+ gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ());
+ gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ());
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags |= (ARGPARSE_FLAG_RESET
+ | ARGPARSE_FLAG_KEEP
+ | ARGPARSE_FLAG_SYS
+ | ARGPARSE_FLAG_USER);
+ while (gnupg_argparser (&pargs, opts, SCDAEMON_NAME EXTSEP_S "conf"))
+ {
+ switch (pargs.r_opt)
+ {
+ case ARGPARSE_CONFFILE:
+ if (debug_argparser)
+ log_info (_("reading options from '%s'\n"),
+ pargs.r_type? pargs.r.ret_str: "[cmdline]");
+ if (pargs.r_type)
+ {
+ xfree (last_configname);
+ last_configname = xstrdup (pargs.r.ret_str);
+ configname = last_configname;
+ }
+ else
+ configname = NULL;
+ break;
+
+ case aGPGConfList: gpgconf_list = 1; break;
+ case aGPGConfTest: gpgconf_list = 2; break;
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: opt.verbose++; break;
+ case oBatch: opt.batch=1; break;
+
+ case oDebug:
+ if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oDebugLevel: debug_level = pargs.r.ret_str; break;
+ case oDebugWait: debug_wait = pargs.r.ret_int; break;
+ case oDebugAllowCoreDump:
+ enable_core_dumps ();
+ allow_coredump = 1;
+ break;
+ case oDebugCCIDDriver:
+#ifdef HAVE_LIBUSB
+ ccid_set_debug_level (ccid_set_debug_level (-1)+1);
+#endif /*HAVE_LIBUSB*/
+ break;
+ case oDebugLogTid:
+ log_set_pid_suffix_cb (tid_log_callback);
+ break;
+ case oDebugAssuanLogCats:
+ set_libassuan_log_cats (pargs.r.ret_ulong);
+ break;
+
+ case oNoGreeting: nogreeting = 1; break;
+ case oNoVerbose: opt.verbose = 0; break;
+ case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+ case oNoDetach: nodetach = 1; break;
+ case oLogFile: logfile = pargs.r.ret_str; break;
+ case oCsh: csh_style = 1; break;
+ case oSh: csh_style = 0; break;
+ case oServer: pipe_server = 1; break;
+ case oMultiServer: pipe_server = 1; multi_server = 1; break;
+ case oDaemon: is_daemon = 1; break;
+
+ case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
+ case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
+ case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
+ case opcscShared: opt.pcsc_shared = 1; break;
+ case oDisableCCID: opt.disable_ccid = 1; break;
+ case oDisableOpenSC: break;
+
+ case oDisablePinpad: opt.disable_pinpad = 1; break;
+
+ case oAllowAdmin: /* Dummy because allow is now the default. */
+ break;
+ case oDenyAdmin: opt.allow_admin = 0; break;
+
+ case oCardTimeout: opt.card_timeout = pargs.r.ret_ulong; break;
+
+ case oDisableApplication:
+ add_to_strlist (&opt.disabled_applications, pargs.r.ret_str);
+ break;
+
+ case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
+
+ case oListenBacklog:
+ listen_backlog = pargs.r.ret_int;
+ break;
+
+ case oNoop: break;
+
+ default:
+ if (configname)
+ pargs.err = ARGPARSE_PRINT_WARNING;
+ else
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ break;
+ }
+ }
+ gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */
+
+ if (!last_configname)
+ config_filename = make_filename (gnupg_homedir (),
+ SCDAEMON_NAME EXTSEP_S "conf",
+ NULL);
+ else
+ {
+ config_filename = last_configname;
+ last_configname = NULL;
+ }
+
+ if (log_get_errorcount(0))
+ exit(2);
+ if (nogreeting )
+ greeting = 0;
+
+ if (greeting)
+ {
+ es_fprintf (es_stderr, "%s %s; %s\n",
+ strusage(11), strusage(13), strusage(14) );
+ es_fprintf (es_stderr, "%s\n", strusage(15) );
+ }
+#ifdef IS_DEVELOPMENT_VERSION
+ log_info ("NOTE: this is a development version!\n");
+#endif
+
+ /* Print a warning if an argument looks like an option. */
+ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+ {
+ int i;
+
+ for (i=0; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == '-')
+ log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+ }
+
+ if (atexit (cleanup))
+ {
+ log_error ("atexit failed\n");
+ cleanup ();
+ exit (1);
+ }
+
+ set_debug (debug_level);
+
+ if (initialize_module_command ())
+ {
+ log_error ("initialization failed\n");
+ cleanup ();
+ exit (1);
+ }
+
+ if (gpgconf_list == 2)
+ scd_exit (0);
+ if (gpgconf_list)
+ {
+ /* List options and default values in the GPG Conf format. */
+ char *filename_esc;
+
+ filename_esc = percent_escape (config_filename, NULL);
+ es_printf ("%s-%s.conf:%lu:\"%s\n",
+ GPGCONF_NAME, SCDAEMON_NAME,
+ GC_OPT_FLAG_DEFAULT, filename_esc);
+ xfree (filename_esc);
+
+ es_printf ("verbose:%lu:\n"
+ "quiet:%lu:\n"
+ "debug-level:%lu:\"none:\n"
+ "log-file:%lu:\n",
+ GC_OPT_FLAG_NONE,
+ GC_OPT_FLAG_NONE,
+ GC_OPT_FLAG_DEFAULT,
+ GC_OPT_FLAG_NONE );
+
+ es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE );
+ es_printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE );
+ es_printf ("pcsc-driver:%lu:\"%s:\n",
+ GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER );
+#ifdef HAVE_LIBUSB
+ es_printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
+#endif
+ es_printf ("deny-admin:%lu:\n", GC_OPT_FLAG_NONE );
+ es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE );
+ es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0);
+ es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE );
+
+ scd_exit (0);
+ }
+
+ /* Now start with logging to a file if this is desired. */
+ if (logfile)
+ {
+ log_set_file (logfile);
+ log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
+ }
+
+ if (debug_wait && pipe_server)
+ {
+ log_debug ("waiting for debugger - my pid is %u .....\n",
+ (unsigned int)getpid());
+ gnupg_sleep (debug_wait);
+ log_debug ("... okay\n");
+ }
+
+ if (pipe_server)
+ {
+ /* This is the simple pipe based server */
+ ctrl_t ctrl;
+ npth_attr_t tattr;
+ int fd = -1;
+
+#ifndef HAVE_W32_SYSTEM
+ {
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &sa, NULL);
+ }
+#endif
+
+ npth_init ();
+ setup_signal_mask ();
+ gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+ /* If --debug-allow-core-dump has been given we also need to
+ switch the working directory to a place where we can actually
+ write. */
+ if (allow_coredump)
+ {
+ if (chdir("/tmp"))
+ log_debug ("chdir to '/tmp' failed: %s\n", strerror (errno));
+ else
+ log_debug ("changed working directory to '/tmp'\n");
+ }
+
+ /* In multi server mode we need to listen on an additional
+ socket. Create that socket now before starting the handler
+ for the pipe connection. This allows that handler to send
+ back the name of that socket. */
+ if (multi_server)
+ {
+ socket_name = create_socket_name (SCDAEMON_SOCK_NAME);
+ fd = FD2INT(create_server_socket (socket_name,
+ &redir_socket_name, &socket_nonce));
+ }
+
+ res = npth_attr_init (&tattr);
+ if (res)
+ {
+ log_error ("error allocating thread attributes: %s\n",
+ strerror (res));
+ scd_exit (2);
+ }
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+ ctrl = xtrycalloc (1, sizeof *ctrl);
+ if ( !ctrl )
+ {
+ log_error ("error allocating connection control data: %s\n",
+ strerror (errno) );
+ scd_exit (2);
+ }
+ ctrl->thread_startup.fd = GNUPG_INVALID_FD;
+ res = npth_create (&pipecon_handler, &tattr, start_connection_thread, ctrl);
+ if (res)
+ {
+ log_error ("error spawning pipe connection handler: %s\n",
+ strerror (res) );
+ xfree (ctrl);
+ scd_exit (2);
+ }
+ npth_setname_np (pipecon_handler, "pipe-connection");
+ npth_attr_destroy (&tattr);
+
+ /* We run handle_connection to wait for the shutdown signal and
+ to run the ticker stuff. */
+ handle_connections (fd);
+ if (fd != -1)
+ close (fd);
+ }
+ else if (!is_daemon)
+ {
+ log_info (_("please use the option '--daemon'"
+ " to run the program in the background\n"));
+ }
+ else
+ { /* Regular server mode */
+ int fd;
+#ifndef HAVE_W32_SYSTEM
+ pid_t pid;
+ int i;
+#endif
+
+ /* Create the socket. */
+ socket_name = create_socket_name (SCDAEMON_SOCK_NAME);
+ fd = FD2INT (create_server_socket (socket_name,
+ &redir_socket_name, &socket_nonce));
+
+
+ fflush (NULL);
+#ifdef HAVE_W32_SYSTEM
+ (void)csh_style;
+ (void)nodetach;
+#else
+ pid = fork ();
+ if (pid == (pid_t)-1)
+ {
+ log_fatal ("fork failed: %s\n", strerror (errno) );
+ exit (1);
+ }
+ else if (pid)
+ { /* we are the parent */
+ char *infostr;
+
+ close (fd);
+
+ /* create the info string: <name>:<pid>:<protocol_version> */
+ if (gpgrt_asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1",
+ socket_name, (ulong) pid) < 0)
+ {
+ log_error ("out of core\n");
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ *socket_name = 0; /* don't let cleanup() remove the socket -
+ the child should do this from now on */
+ if (argc)
+ { /* run the program given on the commandline */
+ if (putenv (infostr))
+ {
+ log_error ("failed to set environment: %s\n",
+ strerror (errno) );
+ kill (pid, SIGTERM );
+ exit (1);
+ }
+ execvp (argv[0], argv);
+ log_error ("failed to run the command: %s\n", strerror (errno));
+ kill (pid, SIGTERM);
+ exit (1);
+ }
+ else
+ {
+ /* Print the environment string, so that the caller can use
+ shell's eval to set it */
+ if (csh_style)
+ {
+ *strchr (infostr, '=') = ' ';
+ es_printf ( "setenv %s;\n", infostr);
+ }
+ else
+ {
+ es_printf ( "%s; export SCDAEMON_INFO;\n", infostr);
+ }
+ xfree (infostr);
+ exit (0);
+ }
+ /* NOTREACHED */
+ } /* end parent */
+
+ /* This is the child. */
+
+ npth_init ();
+ setup_signal_mask ();
+ gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+ /* Detach from tty and put process into a new session. */
+ if (!nodetach )
+ {
+ /* Close stdin, stdout and stderr unless it is the log stream. */
+ for (i=0; i <= 2; i++)
+ {
+ if (!log_test_fd (i) && i != fd )
+ {
+ if ( !close (i)
+ && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+ {
+ log_error ("failed to open '%s': %s\n",
+ "/dev/null", strerror (errno));
+ cleanup ();
+ exit (1);
+ }
+ }
+ }
+
+ if (setsid() == -1)
+ {
+ log_error ("setsid() failed: %s\n", strerror(errno) );
+ cleanup ();
+ exit (1);
+ }
+ }
+
+ {
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGPIPE, &sa, NULL);
+ }
+
+#endif /*!HAVE_W32_SYSTEM*/
+
+ if (gnupg_chdir (gnupg_daemon_rootdir ()))
+ {
+ log_error ("chdir to '%s' failed: %s\n",
+ gnupg_daemon_rootdir (), strerror (errno));
+ exit (1);
+ }
+
+ handle_connections (fd);
+
+ close (fd);
+ }
+
+ xfree (config_filename);
+ return 0;
+}
+
+
+void
+scd_exit (int rc)
+{
+ apdu_prepare_exit ();
+#if 0
+#warning no update_random_seed_file
+ update_random_seed_file();
+#endif
+#if 0
+ /* at this time a bit annoying */
+ if (opt.debug & DBG_MEMSTAT_VALUE)
+ {
+ gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
+ gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
+ }
+ if (opt.debug)
+ gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
+#endif
+ gcry_control (GCRYCTL_TERM_SECMEM );
+ rc = rc? rc : log_get_errorcount(0)? 2 : 0;
+ exit (rc);
+}
+
+
+static void
+scd_init_default_ctrl (ctrl_t ctrl)
+{
+ (void)ctrl;
+}
+
+static void
+scd_deinit_default_ctrl (ctrl_t ctrl)
+{
+ if (!ctrl)
+ return;
+ xfree (ctrl->in_data.value);
+ ctrl->in_data.value = NULL;
+ ctrl->in_data.valuelen = 0;
+}
+
+
+/* Return the name of the socket to be used to connect to this
+ process. If no socket is available, return NULL. */
+const char *
+scd_get_socket_name ()
+{
+ if (socket_name && *socket_name)
+ return socket_name;
+ return NULL;
+}
+
+
+#ifndef HAVE_W32_SYSTEM
+static void
+handle_signal (int signo)
+{
+ switch (signo)
+ {
+ case SIGHUP:
+ log_info ("SIGHUP received - "
+ "re-reading configuration and resetting cards\n");
+/* reread_configuration (); */
+ break;
+
+ case SIGUSR1:
+ log_info ("SIGUSR1 received - printing internal information:\n");
+ /* Fixme: We need to see how to integrate pth dumping into our
+ logging system. */
+ /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
+ app_dump_state ();
+ break;
+
+ case SIGUSR2:
+ log_info ("SIGUSR2 received - no action defined\n");
+ break;
+
+ case SIGCONT:
+ /* Nothing. */
+ log_debug ("SIGCONT received - breaking select\n");
+ break;
+
+ case SIGTERM:
+ if (!shutdown_pending)
+ log_info ("SIGTERM received - shutting down ...\n");
+ else
+ log_info ("SIGTERM received - still %i running threads\n",
+ active_connections);
+ shutdown_pending++;
+ if (shutdown_pending > 2)
+ {
+ log_info ("shutdown forced\n");
+ log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+ cleanup ();
+ scd_exit (0);
+ }
+ break;
+
+ case SIGINT:
+ log_info ("SIGINT received - immediate shutdown\n");
+ log_info( "%s %s stopped\n", strusage(11), strusage(13));
+ cleanup ();
+ scd_exit (0);
+ break;
+
+ default:
+ log_info ("signal %d received - no action defined\n", signo);
+ }
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Create a name for the socket. We check for valid characters as
+ well as against a maximum allowed length for a unix domain socket
+ is done. The function terminates the process in case of an error.
+ Retunrs: Pointer to an allcoated string with the absolute name of
+ the socket used. */
+static char *
+create_socket_name (char *standard_name)
+{
+ char *name;
+
+ name = make_filename (gnupg_socketdir (), standard_name, NULL);
+ if (strchr (name, PATHSEP_C))
+ {
+ log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
+ scd_exit (2);
+ }
+ return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME. Returns the file descriptor
+ or terminates the process in case of an error. If the socket has
+ been redirected the name of the real socket is stored as a malloced
+ string at R_REDIR_NAME. */
+static gnupg_fd_t
+create_server_socket (const char *name, char **r_redir_name,
+ assuan_sock_nonce_t *nonce)
+{
+ struct sockaddr *addr;
+ struct sockaddr_un *unaddr;
+ socklen_t len;
+ gnupg_fd_t fd;
+ int rc;
+
+ xfree (*r_redir_name);
+ *r_redir_name = NULL;
+
+ fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+ if (fd == GNUPG_INVALID_FD)
+ {
+ log_error (_("can't create socket: %s\n"), strerror (errno));
+ scd_exit (2);
+ }
+
+ unaddr = xmalloc (sizeof (*unaddr));
+ addr = (struct sockaddr*)unaddr;
+
+ {
+ int redirected;
+
+ if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
+ {
+ if (errno == ENAMETOOLONG)
+ log_error (_("socket name '%s' is too long\n"), name);
+ else
+ log_error ("error preparing socket '%s': %s\n",
+ name, gpg_strerror (gpg_error_from_syserror ()));
+ scd_exit (2);
+ }
+ if (redirected)
+ {
+ *r_redir_name = xstrdup (unaddr->sun_path);
+ if (opt.verbose)
+ log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
+ }
+ }
+
+ len = SUN_LEN (unaddr);
+
+ rc = assuan_sock_bind (fd, addr, len);
+ if (rc == -1 && errno == EADDRINUSE)
+ {
+ gnupg_remove (unaddr->sun_path);
+ rc = assuan_sock_bind (fd, addr, len);
+ }
+ if (rc != -1
+ && (rc=assuan_sock_get_nonce (addr, len, nonce)))
+ log_error (_("error getting nonce for the socket\n"));
+ if (rc == -1)
+ {
+ log_error (_("error binding socket to '%s': %s\n"),
+ unaddr->sun_path,
+ gpg_strerror (gpg_error_from_syserror ()));
+ assuan_sock_close (fd);
+ scd_exit (2);
+ }
+
+ if (gnupg_chmod (unaddr->sun_path, "-rwx"))
+ log_error (_("can't set permissions of '%s': %s\n"),
+ unaddr->sun_path, strerror (errno));
+
+ if (listen (FD2INT(fd), listen_backlog) == -1)
+ {
+ log_error ("listen(fd, %d) failed: %s\n",
+ listen_backlog, gpg_strerror (gpg_error_from_syserror ()));
+ assuan_sock_close (fd);
+ scd_exit (2);
+ }
+
+ if (opt.verbose)
+ log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
+
+ return fd;
+}
+
+
+
+/* This is the standard connection thread's main function. */
+static void *
+start_connection_thread (void *arg)
+{
+ ctrl_t ctrl = arg;
+
+ if (ctrl->thread_startup.fd != GNUPG_INVALID_FD
+ && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_nonce))
+ {
+ log_info (_("error reading nonce on fd %d: %s\n"),
+ FD2INT(ctrl->thread_startup.fd), strerror (errno));
+ assuan_sock_close (ctrl->thread_startup.fd);
+ xfree (ctrl);
+ return NULL;
+ }
+
+ active_connections++;
+
+ scd_init_default_ctrl (ctrl);
+ if (opt.verbose)
+ log_info (_("handler for fd %d started\n"),
+ FD2INT(ctrl->thread_startup.fd));
+
+ /* If this is a pipe server, we request a shutdown if the command
+ handler asked for it. With the next ticker event and given that
+ no other connections are running the shutdown will then
+ happen. */
+ if (scd_command_handler (ctrl, FD2INT(ctrl->thread_startup.fd))
+ && pipe_server)
+ shutdown_pending = 1;
+
+ if (opt.verbose)
+ log_info (_("handler for fd %d terminated\n"),
+ FD2INT (ctrl->thread_startup.fd));
+
+ scd_deinit_default_ctrl (ctrl);
+ xfree (ctrl);
+
+ if (--active_connections == 0)
+ scd_kick_the_loop ();
+
+ return NULL;
+}
+
+
+void
+scd_kick_the_loop (void)
+{
+ /* Kick the select loop. */
+#ifdef HAVE_W32_SYSTEM
+ int ret = SetEvent (the_event);
+ if (ret == 0)
+ log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
+ w32_strerror (-1));
+#elif defined(HAVE_PSELECT_NO_EINTR)
+ write (notify_fd, "", 1);
+#else
+ int ret = kill (main_thread_pid, SIGCONT);
+ if (ret < 0)
+ log_error ("sending signal for scd_kick_the_loop failed: %s\n",
+ gpg_strerror (gpg_error_from_syserror ()));
+#endif
+}
+
+/* Connection handler loop. Wait for connection requests and spawn a
+ thread after accepting a connection. LISTEN_FD is allowed to be -1
+ in which case this code will only do regular timeouts and handle
+ signals. */
+static void
+handle_connections (int listen_fd)
+{
+ npth_attr_t tattr;
+ struct sockaddr_un paddr;
+ socklen_t plen;
+ fd_set fdset, read_fdset;
+ int nfd;
+ int ret;
+ int fd;
+ struct timespec timeout;
+ struct timespec *t;
+ int saved_errno;
+#ifdef HAVE_W32_SYSTEM
+ HANDLE events[2];
+ unsigned int events_set;
+#else
+ int signo;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ int pipe_fd[2];
+
+ ret = gnupg_create_pipe (pipe_fd);
+ if (ret)
+ {
+ log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
+ return;
+ }
+ notify_fd = pipe_fd[1];
+#endif
+
+ ret = npth_attr_init(&tattr);
+ if (ret)
+ {
+ log_error ("npth_attr_init failed: %s\n", strerror (ret));
+ return;
+ }
+
+ npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+#ifdef HAVE_W32_SYSTEM
+ {
+ HANDLE h, h2;
+ SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+
+ events[0] = the_event = INVALID_HANDLE_VALUE;
+ events[1] = INVALID_HANDLE_VALUE;
+ /* Create event for manual reset, initially non-signaled. Make it
+ * waitable and inheritable. */
+ h = CreateEvent (&sa, TRUE, FALSE, NULL);
+ if (!h)
+ log_error ("can't create scd event: %s\n", w32_strerror (-1) );
+ else if (!DuplicateHandle (GetCurrentProcess(), h,
+ GetCurrentProcess(), &h2,
+ EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
+ {
+ log_error ("setting synchronize for scd_kick_the_loop failed: %s\n",
+ w32_strerror (-1) );
+ CloseHandle (h);
+ }
+ else
+ {
+ CloseHandle (h);
+ events[0] = the_event = h2;
+ }
+ }
+#endif
+
+ FD_ZERO (&fdset);
+ nfd = 0;
+ if (listen_fd != -1)
+ {
+ FD_SET (listen_fd, &fdset);
+ nfd = listen_fd;
+ }
+
+ for (;;)
+ {
+ int periodical_check;
+ int max_fd = nfd;
+
+ if (shutdown_pending)
+ {
+ if (active_connections == 0)
+ break; /* ready */
+
+ /* Do not accept anymore connections but wait for existing
+ connections to terminate. We do this by clearing out all
+ file descriptors to wait for, so that the select will be
+ used to just wait on a signal or timeout event. */
+ FD_ZERO (&fdset);
+ listen_fd = -1;
+ }
+
+ periodical_check = scd_update_reader_status_file ();
+
+ timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+ timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+
+ if (shutdown_pending || periodical_check)
+ t = &timeout;
+ else
+ t = NULL;
+
+ /* POSIX says that fd_set should be implemented as a structure,
+ thus a simple assignment is fine to copy the entire set. */
+ read_fdset = fdset;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+ FD_SET (pipe_fd[0], &read_fdset);
+ if (max_fd < pipe_fd[0])
+ max_fd = pipe_fd[0];
+#else
+ (void)max_fd;
+#endif
+
+#ifndef HAVE_W32_SYSTEM
+ ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
+ npth_sigev_sigmask ());
+ saved_errno = errno;
+
+ while (npth_sigev_get_pending(&signo))
+ handle_signal (signo);
+#else
+ ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t,
+ events, &events_set);
+ saved_errno = errno;
+ if (events_set & 1)
+ continue;
+#endif
+
+ if (ret == -1 && saved_errno != EINTR)
+ {
+ log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+ strerror (saved_errno));
+ npth_sleep (1);
+ continue;
+ }
+
+ if (ret <= 0)
+ /* Timeout. Will be handled when calculating the next timeout. */
+ continue;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+ if (FD_ISSET (pipe_fd[0], &read_fdset))
+ {
+ char buf[256];
+
+ read (pipe_fd[0], buf, sizeof buf);
+ }
+#endif
+
+ if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
+ {
+ ctrl_t ctrl;
+
+ plen = sizeof paddr;
+ fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
+ if (fd == -1)
+ {
+ log_error ("accept failed: %s\n", strerror (errno));
+ }
+ else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
+ {
+ log_error ("error allocating connection control data: %s\n",
+ strerror (errno) );
+ close (fd);
+ }
+ else
+ {
+ char threadname[50];
+ npth_t thread;
+
+ snprintf (threadname, sizeof threadname, "conn fd=%d", fd);
+ ctrl->thread_startup.fd = INT2FD (fd);
+ ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
+ if (ret)
+ {
+ log_error ("error spawning connection handler: %s\n",
+ strerror (ret));
+ xfree (ctrl);
+ close (fd);
+ }
+ else
+ npth_setname_np (thread, threadname);
+ }
+ }
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ if (the_event != INVALID_HANDLE_VALUE)
+ CloseHandle (the_event);
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+ close (pipe_fd[0]);
+ close (pipe_fd[1]);
+#endif
+ cleanup ();
+ log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+ npth_attr_destroy (&tattr);
+}
+
+/* Return the number of active connections. */
+int
+get_active_connection_count (void)
+{
+ return active_connections;
+}
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
new file mode 100644
index 0000000..bb7c1b0
--- /dev/null
+++ b/scd/scdaemon.h
@@ -0,0 +1,145 @@
+/* scdaemon.h - Global definitions for the SCdaemon
+ * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef SCDAEMON_H
+#define SCDAEMON_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_SCD
+#include <gpg-error.h>
+
+#include <time.h>
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "../common/sysutils.h"
+#include "app-common.h"
+
+
+/* To convey some special hash algorithms we use algorithm numbers
+ reserved for application use. */
+#ifndef GCRY_MODULE_ID_USER
+#define GCRY_MODULE_ID_USER 1024
+#endif
+#define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
+
+/* Maximum length of a digest. */
+#define MAX_DIGEST_LEN 64
+
+
+
+/* A large struct name "opt" to keep global flags. */
+EXTERN_UNLESS_MAIN_MODULE
+struct
+{
+ unsigned int debug; /* Debug flags (DBG_foo_VALUE). */
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be as quiet as possible. */
+ int dry_run; /* Don't change any persistent data. */
+ int batch; /* Batch mode. */
+ const char *ctapi_driver; /* Library to access the ctAPI. */
+ const char *pcsc_driver; /* Library to access the PC/SC system. */
+ const char *reader_port; /* NULL or reder port to use. */
+ int disable_ccid; /* Disable the use of the internal CCID driver. */
+ int disable_pinpad; /* Do not use a pinpad. */
+ int enable_pinpad_varlen; /* Use variable length input for pinpad. */
+ int allow_admin; /* Allow the use of admin commands for certain
+ cards. */
+ int pcsc_shared; /* Use shared PC/SC access. */
+ strlist_t disabled_applications; /* Card applications we do not
+ want to use. */
+ unsigned long card_timeout; /* Disconnect after N seconds of inactivity. */
+} opt;
+
+
+#define DBG_MPI_VALUE 2 /* debug mpi details */
+#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
+#define DBG_CARD_VALUE 16 /* debug card info */
+#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE 64 /* debug the caching */
+#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
+#define DBG_HASHING_VALUE 512 /* debug hashing operations */
+#define DBG_IPC_VALUE 1024
+#define DBG_CARD_IO_VALUE 2048 /* debug card I/O (e.g. APDUs). */
+#define DBG_READER_VALUE 4096 /* Trace reader related functions. */
+
+#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
+#define DBG_CARD (opt.debug & DBG_CARD_VALUE)
+#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
+#define DBG_READER (opt.debug & DBG_READER_VALUE)
+
+struct server_local_s;
+struct app_ctx_s;
+
+struct server_control_s
+{
+ /* Private data used to fire up the connection thread. We use this
+ structure do avoid an extra allocation for just a few bytes. */
+ struct {
+ gnupg_fd_t fd;
+ } thread_startup;
+
+ /* Local data of the server; used only in command.c. */
+ struct server_local_s *server_local;
+
+ /* The application context used with this connection or NULL if none
+ associated. Note that this is shared with the other connections:
+ All connections accessing the same reader are using the same
+ application context. */
+ struct app_ctx_s *app_ctx;
+
+ /* Helper to store the value we are going to sign */
+ struct
+ {
+ unsigned char *value;
+ int valuelen;
+ } in_data;
+};
+
+
+/*-- scdaemon.c --*/
+void scd_exit (int rc);
+const char *scd_get_socket_name (void);
+
+/*-- command.c --*/
+gpg_error_t initialize_module_command (void);
+int scd_command_handler (ctrl_t, int);
+void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
+ const char *serialno, const char *idstr);
+void send_status_info (ctrl_t ctrl, const char *keyword, ...)
+ GPGRT_ATTR_SENTINEL(1);
+gpg_error_t send_status_direct (ctrl_t ctrl, const char *keyword,
+ const char *args);
+gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword,
+ const char *format, ...) GPGRT_ATTR_PRINTF(3,4);
+
+void popup_prompt (void *opaque, int on);
+void send_client_notifications (app_t app, int removal);
+void scd_kick_the_loop (void);
+int get_active_connection_count (void);
+
+/*-- app.c --*/
+int scd_update_reader_status_file (void);
+
+#endif /*SCDAEMON_H*/
diff --git a/scd/scdaemon.w32-manifest.in b/scd/scdaemon.w32-manifest.in
new file mode 100644
index 0000000..22dabb6
--- /dev/null
+++ b/scd/scdaemon.w32-manifest.in
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<description>GNU Privacy Guard (Scmartcard daemon)</description>
+<assemblyIdentity
+ type="win32"
+ name="GnuPG.scdaemon"
+ version="@BUILD_VERSION@"
+ />
+<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/><!-- 8 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/><!-- 7 -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista -->
+ </application>
+</compatibility>
+</assembly>