diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index 81af28105..1271994c4 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -1,770 +1,776 @@ @c Copyright (C) 2002 Free Software Foundation, Inc. @c This is part of the GnuPG manual. @c For copying conditions, see the file gnupg.texi. @include defs.inc @node Invoking SCDAEMON @chapter Invoking the SCDAEMON @cindex SCDAEMON command options @cindex command options @cindex options, SCDAEMON command @manpage scdaemon.1 @ifset manverb .B scdaemon \- Smartcard daemon for the GnuPG system @end ifset @mansect synopsis @ifset manverb .B scdaemon .RB [ \-\-homedir .IR dir ] .RB [ \-\-options .IR file ] .RI [ options ] .B \-\-server .br .B scdaemon .RB [ \-\-homedir .IR dir ] .RB [ \-\-options .IR file ] .RI [ options ] .B \-\-daemon .RI [ command_line ] @end ifset @mansect description The @command{scdaemon} is a daemon to manage smartcards. It is usually invoked by @command{gpg-agent} and in general not used directly. @manpause @xref{Option Index}, for an index to @command{scdaemon}'s commands and options. @mancont @menu * Scdaemon Commands:: List of all commands. * Scdaemon Options:: List of all options. * Card applications:: Description of card applications. * Scdaemon Configuration:: Configuration files. * Scdaemon Examples:: Some usage examples. * Scdaemon Protocol:: The protocol the daemon uses. @end menu @mansect commands @node Scdaemon Commands @section Commands Commands are not distinguished from options except for the fact that only one command is allowed. @table @gnupgtabopt @item --version @opindex version Print the program version and licensing information. Note that you cannot abbreviate this command. @item --help, -h @opindex help Print a usage message summarizing the most useful command-line options. Note that you cannot abbreviate this command. @item --dump-options @opindex dump-options Print a list of all available options and commands. Note that you cannot abbreviate this command. @item --server @opindex server Run in server mode and wait for commands on the @code{stdin}. The default mode is to create a socket and listen for commands there. @item --multi-server @opindex multi-server Run in server mode and wait for commands on the @code{stdin} as well as on an additional Unix Domain socket. The server command @code{GETINFO} may be used to get the name of that extra socket. @item --daemon @opindex daemon Run the program in the background. This option is required to prevent it from being accidentally running in the background. @end table @mansect options @node Scdaemon Options @section Option Summary @table @gnupgtabopt @item --options @var{file} @opindex options Reads configuration from @var{file} instead of from the default per-user configuration file. The default configuration file is named @file{scdaemon.conf} and expected in the @file{.gnupg} directory directly below the home directory of the user. @include opt-homedir.texi @item -v @item --verbose @opindex v @opindex verbose Outputs additional information while running. You can increase the verbosity by giving several verbose commands to @command{gpgsm}, such as @samp{-vv}. @item --debug-level @var{level} @opindex debug-level Select the debug level for investigating problems. @var{level} may be a numeric value or a keyword: @table @code @item none No debugging at all. A value of less than 1 may be used instead of the keyword. @item basic Some basic debug messages. A value between 1 and 2 may be used instead of the keyword. @item advanced More verbose debug messages. A value between 3 and 5 may be used instead of the keyword. @item expert Even more detailed messages. A value between 6 and 8 may be used instead of the keyword. @item guru All of the debug messages you can get. A value greater than 8 may be used instead of the keyword. The creation of hash tracing files is only enabled if the keyword is used. @end table How these messages are mapped to the actual debugging flags is not specified and may change with newer releases of this program. They are however carefully selected to best aid in debugging. @quotation Note All debugging options are subject to change and thus should not be used by any application program. As the name says, they are only used as helpers to debug problems. @end quotation @item --debug @var{flags} @opindex debug This option is only useful for debugging and the behavior may change at any time without notice. FLAGS are bit encoded and may be given in usual C-Syntax. The currently defined bits are: @table @code @item 0 (1) command I/O @item 1 (2) values of big number integers @item 2 (4) low level crypto operations @item 5 (32) memory allocation @item 6 (64) caching @item 7 (128) show memory statistics @item 9 (512) write hashed data to files named @code{dbgmd-000*} @item 10 (1024) trace Assuan protocol. See also option @option{--debug-assuan-log-cats}. @item 11 (2048) trace APDU I/O to the card. This may reveal sensitive data. @item 12 (4096) trace some card reader related function calls. @end table @item --debug-all @opindex debug-all Same as @code{--debug=0xffffffff} @item --debug-wait @var{n} @opindex debug-wait When running in server mode, wait @var{n} seconds before entering the actual processing loop and print the pid. This gives time to attach a debugger. @item --debug-ccid-driver @opindex debug-wait Enable debug output from the included CCID driver for smartcards. Using this option twice will also enable some tracing of the T=1 protocol. Note that this option may reveal sensitive data. @item --debug-disable-ticker @opindex debug-disable-ticker This option disables all ticker functions like checking for card insertions. @item --debug-allow-core-dump @opindex debug-allow-core-dump For security reasons we won't create a core dump when the process aborts. For debugging purposes it is sometimes better to allow core dump. This option enables it and also changes the working directory to @file{/tmp} when running in @option{--server} mode. @item --debug-log-tid @opindex debug-log-tid This option appends a thread ID to the PID in the log output. @item --debug-assuan-log-cats @var{cats} @opindex debug-assuan-log-cats @efindex ASSUAN_DEBUG Changes the active Libassuan logging categories to @var{cats}. The value for @var{cats} is an unsigned integer given in usual C-Syntax. A value of 0 switches to a default category. If this option is not used the categories are taken from the environment variable @code{ASSUAN_DEBUG}. Note that this option has only an effect if the Assuan debug flag has also been with the option @option{--debug}. For a list of categories see the Libassuan manual. @item --no-detach @opindex no-detach Don't detach the process from the console. This is mainly useful for debugging. @item --listen-backlog @var{n} @opindex listen-backlog Set the size of the queue for pending connections. The default is 64. This option has an effect only if @option{--multi-server} is also used. @item --log-file @var{file} @opindex log-file Append all logging output to @var{file}. This is very helpful in seeing what the agent actually does. Use @file{socket://} to log to socket. +@item --pcsc-shared +@opindex pcsc-shared +Use shared mode to access the card via PC/SC. This is a somewhat +dangerous option because Scdaemon assumes exclusivbe access to teh +card and for example caches certain information from the card. Use +this option only if you know what you are doing. @item --pcsc-driver @var{library} @opindex pcsc-driver Use @var{library} to access the smartcard reader. The current default is @file{libpcsclite.so}. Instead of using this option you might also want to install a symbolic link to the default file name (e.g. from @file{libpcsclite.so.1}). @item --ctapi-driver @var{library} @opindex ctapi-driver Use @var{library} to access the smartcard reader. The current default is @file{libtowitoko.so}. Note that the use of this interface is deprecated; it may be removed in future releases. @item --disable-ccid @opindex disable-ccid Disable the integrated support for CCID compliant readers. This allows falling back to one of the other drivers even if the internal CCID driver can handle the reader. Note, that CCID support is only available if libusb was available at build time. @item --reader-port @var{number_or_string} @opindex reader-port This option may be used to specify the port of the card terminal. A value of 0 refers to the first serial device; add 32768 to access USB devices. The default is 32768 (first USB device). PC/SC or CCID readers might need a string here; run the program in verbose mode to get a list of available readers. The default is then the first reader found. To get a list of available CCID readers you may use this command: @cartouche @smallexample echo scd getinfo reader_list \ | gpg-connect-agent --decode | awk '/^D/ @{print $2@}' @end smallexample @end cartouche @item --card-timeout @var{n} @opindex card-timeout If @var{n} is not 0 and no client is actively using the card, the card will be powered down after @var{n} seconds. Powering down the card avoids a potential risk of damaging a card when used with certain cheap readers. This also allows applications that are not aware of Scdaemon to access the card. The disadvantage of using a card timeout is that accessing the card takes longer and that the user needs to enter the PIN again after the next power up. Note that with the current version of Scdaemon the card is powered down immediately at the next timer tick for any value of @var{n} other than 0. @item --enable-pinpad-varlen @opindex enable-pinpad-varlen Please specify this option when the card reader supports variable length input for pinpad (default is no). For known readers (listed in ccid-driver.c and apdu.c), this option is not needed. Note that if your card reader doesn't supports variable length input but you want to use it, you need to specify your pinpad request on your card. @item --disable-pinpad @opindex disable-pinpad Even if a card reader features a pinpad, do not try to use it. @item --deny-admin @opindex deny-admin @opindex allow-admin This option disables the use of admin class commands for card applications where this is supported. Currently we support it for the OpenPGP card. This option is useful to inhibit accidental access to admin class command which could ultimately lock the card through wrong PIN numbers. Note that GnuPG versions older than 2.0.11 featured an @option{--allow-admin} option which was required to use such admin commands. This option has no more effect today because the default is now to allow admin commands. @item --disable-application @var{name} @opindex disable-application This option disables the use of the card application named @var{name}. This is mainly useful for debugging or if a application with lower priority should be used by default. @end table All the long options may also be given in the configuration file after stripping off the two leading dashes. @mansect card applications @node Card applications @section Description of card applications @command{scdaemon} supports the card applications as described below. @menu * OpenPGP Card:: The OpenPGP card application * NKS Card:: The Telesec NetKey card application * DINSIG Card:: The DINSIG card application * PKCS#15 Card:: The PKCS#15 card application * Geldkarte Card:: The Geldkarte application * SmartCard-HSM:: The SmartCard-HSM application * Undefined Card:: The Undefined stub application @end menu @node OpenPGP Card @subsection The OpenPGP card application ``openpgp'' This application is currently only used by @command{gpg} but may in future also be useful with @command{gpgsm}. Version 1 and version 2 of the card is supported. @noindent The specifications for these cards are available at@* @uref{http://g10code.com/docs/openpgp-card-1.0.pdf} and@* @uref{http://g10code.com/docs/openpgp-card-2.0.pdf}. @node NKS Card @subsection The Telesec NetKey card ``nks'' This is the main application of the Telesec cards as available in Germany. It is a superset of the German DINSIG card. The card is used by @command{gpgsm}. @node DINSIG Card @subsection The DINSIG card application ``dinsig'' This is an application as described in the German draft standard @emph{DIN V 66291-1}. It is intended to be used by cards supporting the German signature law and its bylaws (SigG and SigV). @node PKCS#15 Card @subsection The PKCS#15 card application ``p15'' This is common framework for smart card applications. It is used by @command{gpgsm}. @node Geldkarte Card @subsection The Geldkarte card application ``geldkarte'' This is a simple application to display information of a German Geldkarte. The Geldkarte is a small amount debit card application which comes with almost all German banking cards. @node SmartCard-HSM @subsection The SmartCard-HSM card application ``sc-hsm'' This application adds read-only support for keys and certificates stored on a @uref{http://www.smartcard-hsm.com, SmartCard-HSM}. To generate keys and store certificates you may use @uref{https://github.com/OpenSC/OpenSC/wiki/SmartCardHSM, OpenSC} or the tools from @uref{http://www.openscdp.org, OpenSCDP}. The SmartCard-HSM cards requires a card reader that supports Extended Length APDUs. @node Undefined Card @subsection The Undefined card application ``undefined'' This is a stub application to allow the use of the APDU command even if no supported application is found on the card. This application is not used automatically but must be explicitly requested using the SERIALNO command. @c ******************************************* @c *************** **************** @c *************** FILES **************** @c *************** **************** @c ******************************************* @mansect files @node Scdaemon Configuration @section Configuration files There are a few configuration files to control certain aspects of @command{scdaemons}'s operation. Unless noted, they are expected in the current home directory (@pxref{option --homedir}). @table @file @item scdaemon.conf @cindex scdaemon.conf This is the standard configuration file read by @command{scdaemon} on startup. It may contain any valid long option; the leading two dashes may not be entered and the option may not be abbreviated. This default name may be changed on the command line (@pxref{option --options}). @item scd-event @cindex scd-event If this file is present and executable, it will be called on every card reader's status change. An example of this script is provided with the distribution @item reader_@var{n}.status This file is created by @command{scdaemon} to let other applications now about reader status changes. Its use is now deprecated in favor of @file{scd-event}. @end table @c @c Examples @c @mansect examples @node Scdaemon Examples @section Examples @c man begin EXAMPLES @example $ scdaemon --server -v @end example @c man end @c @c Assuan Protocol @c @manpause @node Scdaemon Protocol @section Scdaemon's Assuan Protocol The SC-Daemon should be started by the system to provide access to external tokens. Using Smartcards on a multi-user system does not make much sense except for system services, but in this case no regular user accounts are hosted on the machine. A client connects to the SC-Daemon by connecting to the socket named @file{@value{LOCALRUNDIR}/scdaemon/socket}, configuration information is read from @var{@value{SYSCONFDIR}/scdaemon.conf} Each connection acts as one session, SC-Daemon takes care of synchronizing access to a token between sessions. @menu * Scdaemon SERIALNO:: Return the serial number. * Scdaemon LEARN:: Read all useful information from the card. * Scdaemon READCERT:: Return a certificate. * Scdaemon READKEY:: Return a public key. * Scdaemon PKSIGN:: Signing data with a Smartcard. * Scdaemon PKDECRYPT:: Decrypting data with a Smartcard. * Scdaemon GETATTR:: Read an attribute's value. * Scdaemon SETATTR:: Update an attribute's value. * Scdaemon WRITEKEY:: Write a key to a card. * Scdaemon GENKEY:: Generate a new key on-card. * Scdaemon RANDOM:: Return random bytes generated on-card. * Scdaemon PASSWD:: Change PINs. * Scdaemon CHECKPIN:: Perform a VERIFY operation. * Scdaemon RESTART:: Restart connection * Scdaemon APDU:: Send a verbatim APDU to the card @end menu @node Scdaemon SERIALNO @subsection Return the serial number This command should be used to check for the presence of a card. It is special in that it can be used to reset the card. Most other commands will return an error when a card change has been detected and the use of this function is therefore required. Background: We want to keep the client clear of handling card changes between operations; i.e. the client can assume that all operations are done on the same card unless he call this function. @example SERIALNO @end example Return the serial number of the card using a status response like: @example S SERIALNO D27600000000000000000000 @end example The serial number is the hex encoded value identified by the @code{0x5A} tag in the GDO file (FIX=0x2F02). @node Scdaemon LEARN @subsection Read all useful information from the card @example LEARN [--force] @end example Learn all useful information of the currently inserted card. When used without the @option{--force} option, the command might do an INQUIRE like this: @example INQUIRE KNOWNCARDP @end example The client should just send an @code{END} if the processing should go on or a @code{CANCEL} to force the function to terminate with a cancel error message. The response of this command is a list of status lines formatted as this: @example S KEYPAIRINFO @var{hexstring_with_keygrip} @var{hexstring_with_id} @end example If there is no certificate yet stored on the card a single "X" is returned in @var{hexstring_with_keygrip}. @node Scdaemon READCERT @subsection Return a certificate @example READCERT @var{hexified_certid}|@var{keyid} @end example This function is used to read a certificate identified by @var{hexified_certid} from the card. With OpenPGP cards the keyid @code{OpenPGP.3} may be used to read the certificate of version 2 cards. @node Scdaemon READKEY @subsection Return a public key @example READKEY @var{hexified_certid} @end example Return the public key for the given cert or key ID as an standard S-Expression. @node Scdaemon PKSIGN @subsection Signing data with a Smartcard To sign some data the caller should use the command @example SETDATA @var{hexstring} @end example to tell @command{scdaemon} about the data to be signed. The data must be given in hex notation. The actual signing is done using the command @example PKSIGN @var{keyid} @end example where @var{keyid} is the hexified ID of the key to be used. The key id may have been retrieved using the command @code{LEARN}. If another hash algorithm than SHA-1 is used, that algorithm may be given like: @example PKSIGN --hash=@var{algoname} @var{keyid} @end example With @var{algoname} are one of @code{sha1}, @code{rmd160} or @code{md5}. @node Scdaemon PKDECRYPT @subsection Decrypting data with a Smartcard To decrypt some data the caller should use the command @example SETDATA @var{hexstring} @end example to tell @command{scdaemon} about the data to be decrypted. The data must be given in hex notation. The actual decryption is then done using the command @example PKDECRYPT @var{keyid} @end example where @var{keyid} is the hexified ID of the key to be used. If the card is aware of the apdding format a status line with padding information is send before the plaintext data. The key for this status line is @code{PADDING} with the only defined value being 0 and meaning padding has been removed. @node Scdaemon GETATTR @subsection Read an attribute's value TO BE WRITTEN. @node Scdaemon SETATTR @subsection Update an attribute's value TO BE WRITTEN. @node Scdaemon WRITEKEY @subsection Write a key to a card @example WRITEKEY [--force] @var{keyid} @end example This command is used to store a secret key on a smartcard. The allowed keyids depend on the currently selected smartcard application. The actual keydata is requested using the inquiry @code{KEYDATA} and need to be provided without any protection. With @option{--force} set an existing key under this @var{keyid} will get overwritten. The key data is expected to be the usual canonical encoded S-expression. A PIN will be requested in most cases. This however depends on the actual card application. @node Scdaemon GENKEY @subsection Generate a new key on-card TO BE WRITTEN. @node Scdaemon RANDOM @subsection Return random bytes generated on-card TO BE WRITTEN. @node Scdaemon PASSWD @subsection Change PINs @example PASSWD [--reset] [--nullpin] @var{chvno} @end example Change the PIN or reset the retry counter of the card holder verification vector number @var{chvno}. The option @option{--nullpin} is used to initialize the PIN of TCOS cards (6 byte NullPIN only). @node Scdaemon CHECKPIN @subsection Perform a VERIFY operation @example CHECKPIN @var{idstr} @end example Perform a VERIFY operation without doing anything else. This may be used to initialize a the PIN cache earlier to long lasting operations. Its use is highly application dependent: @table @strong @item OpenPGP 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. @var{idstr} is the usual card's serial number in hex notation; an optional fingerprint part will get ignored. There is however a special mode if @var{idstr} is suffixed with the literal string @code{[CHV3]}: In this case the Admin PIN is checked if and only if the retry counter is still at 3. @end table @node Scdaemon RESTART @subsection Perform a RESTART operation @example RESTART @end example Restart the current connection; this is a kind of warm reset. It deletes the context used by this connection but does not actually reset the card. This is used by gpg-agent to reuse a primary pipe connection and may be used by clients to backup from a conflict in the serial command; i.e. to select another application. @node Scdaemon APDU @subsection Send a verbatim APDU to the card @example APDU [--atr] [--more] [--exlen[=@var{n}]] [@var{hexstring}] @end example Send an APDU to the current reader. This command bypasses the high level functions and sends the data directly to the card. @var{hexstring} is expected to be a proper APDU. If @var{hexstring} is not given no commands are send to the card; However the command will implicitly check whether the card is ready for use. Using the option @code{--atr} returns the ATR of the card as a status message before any data like this: @example S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1 @end example Using the option @code{--more} handles the card status word MORE_DATA (61xx) and concatenate all responses to one block. Using the option @code{--exlen} the returned APDU may use extended length up to N bytes. If N is not given a default value is used (currently 4096). @mansect see also @ifset isman @command{gpg-agent}(1), @command{gpgsm}(1), @command{gpg2}(1) @end ifset @include see-also-note.texi diff --git a/scd/apdu.c b/scd/apdu.c index 3bdb509b3..6850aae49 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1,3342 +1,3343 @@ /* 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 . */ /* 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 #include #include #include #include #include #include #ifdef USE_NPTH # include # include # include #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 "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 /* 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 context; 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_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 #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_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 */ 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; 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 (reader_table[slot].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) - && !(reader_table[slot].pcsc.current_state & PCSC_STATE_INUSE) ) + && (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) { pcsc_release_context (reader_table[slot].pcsc.context); return 0; } /* Connect a PC/SC card. */ static int connect_pcsc_card (int slot) { long err; 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 (reader_table[slot].pcsc.context, reader_table[slot].rdrname, - PCSC_SHARE_EXCLUSIVE, + 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); } 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; 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; } /* Open the PC/SC reader without using the wrapper. 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; char *rdrname = NULL; pcsc_dword_t nreader; char *p; slot = new_reader_slot (); if (slot == -1) return -1; /* Fixme: Allocating a context for each slot is not required. One global context should be sufficient. */ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL, &reader_table[slot].pcsc.context); if (err) { log_error ("pcsc_establish_context failed: %s (0x%lx)\n", pcsc_error_string (err), err); reader_table[slot].used = 0; unlock_slot (slot); return -1; } err = pcsc_list_readers (reader_table[slot].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"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; unlock_slot (slot); return -1 /*SW_HOST_OUT_OF_CORE*/; } err = pcsc_list_readers (reader_table[slot].pcsc.context, NULL, list, &nreader); } if (err) { log_error ("pcsc_list_readers failed: %s (0x%lx)\n", pcsc_error_string (err), err); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; xfree (list); unlock_slot (slot); return -1; } p = list; while (nreader) { if (!*p && !p[1]) break; log_info ("detected reader '%s'\n", p); if (nreader < (strlen (p)+1)) { log_error ("invalid response from pcsc_list_readers\n"); break; } if (!rdrname && portstr && !strncmp (p, portstr, strlen (portstr))) rdrname = p; nreader -= strlen (p)+1; p += strlen (p) + 1; } if (!rdrname) rdrname = list; reader_table[slot].rdrname = xtrystrdup (rdrname); if (!reader_table[slot].rdrname) { log_error ("error allocating memory for reader name\n"); pcsc_release_context (reader_table[slot].pcsc.context); reader_table[slot].used = 0; unlock_slot (slot); return -1; } 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); 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. */ 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); } /* Open the reader and return an internal slot number or -1 on error. If PORTSTR is NULL we default to a suitable port (for ctAPI: the first USB reader. For PC/SC the first listed reader). */ static int apdu_open_one_reader (const char *portstr) { static int pcsc_api_loaded; int slot; if (DBG_READER) log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr); /* Lets try the PC/SC API */ if (!pcsc_api_loaded) { void *handle; handle = dlopen (opt.pcsc_driver, RTLD_LAZY); if (!handle) { log_error ("apdu_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_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 ("apdu_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; } slot = open_pcsc_reader (portstr); if (DBG_READER) log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot); return slot; } 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 = apdu_open_one_reader (dl->portstr); } else slot = -1; } else #endif { /* PC/SC readers. */ if (app_empty && dl->idx == 0) { dl->idx++; slot = apdu_open_one_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); 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; 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. */ 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)) 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; } 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; 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)) 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) 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 = trylock_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; } 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; } gpg_error_t apdu_init (void) { #ifdef USE_NPTH gpg_error_t err; int i; 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/app-openpgp.c b/scd/app-openpgp.c index efd8d79bb..4ac7f5d72 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,5439 +1,5439 @@ /* 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 . */ /* 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 #include #include #include #include #include #include #include #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 void 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 "Dangerous Things"; case 0x000B: return "Feitian Technologies"; 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 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 (gpg_err_code_from_errno (errno)); 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< 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= Where PINPAD_REQUEST is in the format of: or ,. 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) + 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, 0, 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); 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 rsa" ECC: "--force " */ 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 public exponent 0xC1 prime p 0xC2 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 { 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 "[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 void parse_algorithm_attribute (app_t app, int keyno) { unsigned char *buffer; size_t buflen; void *relptr; const char desc[3][5] = {"sign", "encr", "auth"}; 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; } if (buflen < 1) { log_error ("error reading DO 0x%02X\n", 0xc1+keyno); xfree (relptr); return; } 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 (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: "); 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); } /* 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; int rc; 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. */ rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001); if (!rc) { 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. */ rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen); if (rc) 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) { rc = gpg_error (gpg_err_code_from_errno (errno)); 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"); 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" ); 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); parse_algorithm_attribute (app, 0); parse_algorithm_attribute (app, 1); parse_algorithm_attribute (app, 2); 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 (rc) do_deinit (app); return rc; } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 6db9c1b7d..862278b5c 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -1,1435 +1,1438 @@ /* scdaemon.c - The GnuPG Smartcard Daemon * Copyright (C) 2001-2002, 2004-2005, 2007-2009 Free Software Foundation, Inc. * Copyright (C) 2001-2002, 2004-2005, 2007-2014 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 . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_W32_SYSTEM #include #include #endif /*HAVE_W32_SYSTEM*/ #include #include #include #define INCLUDED_BY_MAIN_MODULE 1 #define GNUPG_COMMON_NEED_AFLOCAL #include "scdaemon.h" #include #include #include /* 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_group (301, N_("@Options:\n ")), 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 (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), ARGPARSE_s_n (oSh, "sh", N_("sh-style command output")), ARGPARSE_s_n (oCsh, "csh", N_("csh-style command output")), ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), 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_n (oNoDetach, "no-detach", N_("do not detach from the console")), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a log to FILE")), 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 (oAllowAdmin, "allow-admin", "@"), ARGPARSE_s_n (oDenyAdmin, "deny-admin", N_("deny the use of admin card commands")), ARGPARSE_s_s (oDisableApplication, "disable-application", "@"), ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen", N_("use variable length input for pinpad")), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"), ARGPARSE_noconffile (oNoOptions, "no-options", "@"), /* 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_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: :: */ 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 ("SetEvent 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; 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 index 336013805..3ba1b9ad0 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -1,142 +1,143 @@ /* 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 . */ #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 #include #include #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_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 #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_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*/