opensc/src/libopensc/card-belpic.c

1605 lines
50 KiB
C
Raw Blame History

/*
* card-belpic.c: Support for Belgium EID card
*
* Copyright (C) 2003, Zetes Belgium
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* About the Belpic (Belgian Personal Identity Card) card
*
* The Belpic card is a Cyberflex Java card, so you normaly communicate
* with an applet running on the card. In order to support a pkcs15 file
* structure, an applet (the Belpic applet) has been build that emulates
* this. So the card's behaviour is specific for this Belpic applet, that's
* why a separate driver has been made.
*
* The card contains the citizen's ID data (name, address, photo, ...) and
* her keys and certs. The ID data are in a seperate directory on the card and
* are not handled by this software. For the cryptographic data (keys and certs)
* a pkcs#15 structure has been chosen and they can be accessed and used
* by the OpenSC software.
*
* The current situation about the cryptographic data is: there is 1 PIN
* that protects 2 private keys and corresponding certs. Then there is a
* CA cert and the root cert. The first key (Auth Key) can be used for
* authentication, the second one (NonRep Key) for non repudation purposes
* (so it can be used as an alternative to manual signatures).
*
* There are some special things to note, which all have some consequences:
* (1) the SELECT FILE command doesn't return any FCI (file length, type, ...)
* (2) the NonRep key needs a VERIFY PIN before a signature can be done with it
* (3) pin pad readers had to be supported by a proprietory interface (as at
* that moment no other solution was known/avaiable/ready)
* The consequences are:
*
* For (1): we let the SELECT FILE command return that the file length is
* a fixed large number and that each file is a transparant working EF
* (except the root dir 3F 00). This way however, there is a problem with the
* sc_read_binary() function that will only stop reading untill it receivces
* a 0. Therefore, we use the 'next_idx' trick. Or, if that might fail
* and so a READ BINARY past the end of file is done, length 0 is returned
* instead of an error code.
*
* For (2), we decided that a GUI for asking the PIN would be the best
* thing to do (another option would be to make 2 virtual slots but that
* causes other problems and is less user-friendly). A GUI being popped up
* by the pkcs11 lib before each NonRep signature has another important
* security advantage: applications that cache the PIN can't silently do
* a NonRep signature because there will allways be the GUI.
*
* For (3), we link dynamically against a pin pad lib (DLL) that implements the
* proprietory API for a specific pin pad. For each pin pad reader (identified
* by it's PC/SC reader name), a pin pad lib correspondends. Some reader/lib
* name pairs are hardcoded, and others can be added in the config file.
* Note that there's also a GUI used in this case: if a signature with the
* NonRep key is done: a dialog box is shown that asks the user to enter
* her PIN on the pin pad reader in order to make a legally valid signature.
*
* Further the (current) Belpic card as quite some limitations:
* no key pair generation or update of data except after establishing a Secure
* Channel or CTV-authentication (which can only be done at the municipalities),
* no encryption. The result is that only a very limited amount of functions
* is/had to be implemented to get the pkcs11 library working.
*
* About the belpic_set_language: the RA-PC software (including the pkcs11 lib)
* in the Brussels' communities should be able to change the language of the GUI
* messages. So the language set by this function takes priority on all other
* language-selection functionality.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "internal.h"
#include "log.h"
#ifdef BELPIC_PIN_PAD
#ifndef HAVE_GUI
#define HAVE_GUI
#endif
#endif
#ifdef BELPIC_PIN_PAD
#include "winscard.h"
#include "scr.h"
#endif
/* These defines are disabled for OpenSC */
#if 0
#define BELPIC_SET_LANG
#define GET_LANG_FROM_CARD
#define HAVE_ALLOW_SSO
#endif
#ifdef HAVE_GUI
#include "scgui.h"
#ifndef SCR_USAGE_SIGN
#define SCR_USAGE_SIGN 2 /* in scr.h */
#endif
#ifndef SCR_USAGE_AUTH
#define SCR_USAGE_AUTH 1
#endif
#endif
/* To be removed */
#include <time.h>
static long t1, t2, tot_read = 0, tot_dur = 0, dur;
#define BELPIC_VERSION "1.4"
/* Most of the #defines here are also present in the pkcs15 files, but
* because this driver has no access to them, it's hardcoded here. If
* other Belpic cards with other 'settings' appear, we'll have to move
* these #defines to the struct belpic_priv_data */
#define BELPIC_MAX_FILE_SIZE 65535
#define BELPIC_PIN_BUF_SIZE 8
#define BELPIC_MIN_USER_PIN_LEN 4
#define BELPIC_MAX_USER_PIN_LEN 12
#define BELPIC_PIN_ENCODING SC_PIN_ENCODING_GLP
#define BELPIC_PAD_CHAR 0xFF
#define BELPIC_KEY_REF_NONREP 0x83
/* Used for a trick in select file and read binary */
static size_t next_idx = (size_t)-1;
static struct sc_atr_table belpic_atrs[] = {
/* Applet V1.1 */
{ "3B:98:13:40:0A:A5:03:01:01:01:AD:13:11", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
/* Applet V1.0 with new EMV-compatible ATR */
{ "3B:98:94:40:0A:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
/* Applet beta 5 + V1.0 */
{ "3B:98:94:40:FF:A5:03:01:01:01:AD:13:10", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
#if 0
/* Applet beta 3 + 4 */
{ "3B:98:11:40:FF:A5:03:01:01:01:AD:13:04", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
/* Applet beta 2 */
{ "3B:68:00:00:29:05:01:02:01:AD:13:03", NULL, NULL, SC_CARD_TYPE_BELPIC_EID, 0, NULL },
#endif
{ NULL, NULL, NULL, 0, 0, NULL }
};
struct belpic_priv_data {
int lang;
int options;
#ifdef BELPIC_PIN_PAD
FARPROC scr_init;
FARPROC scr_verify_pin;
FARPROC scr_change_pin;
char szPinPadDll[64];
#endif
};
#define DRVDATA(card) ((struct belpic_priv_data *) ((card)->drv_data))
/* Single Sign On */
#ifdef HAVE_ALLOW_SSO
#define SSO_OK(drv) ((drv)->allow_sso)
#else
#define SSO_OK(drv) 0
#endif
static struct sc_card_operations belpic_ops;
static struct sc_card_driver belpic_drv = {
"Belpic cards",
"belpic",
&belpic_ops,
NULL, 0, NULL
};
static const struct sc_card_operations *iso_ops = NULL;
#define LNG_ENG 0
#define LNG_DUTCH 1
#define LNG_FRENCH 2
#define LNG_GERMAN 3
#define LNG_NONE 0xFFFF
#ifdef BELPIC_PIN_PAD
/* Option flags from the config file */
#define PP_MSG_AUTH_PIN 0x00000001
#define PP_MSG_WRONG_PIN 0x00000002
#define PP_MSG_CHANGEPIN_MISMATCH 0x00000004
#define PP_MSG_PIN_BLOCKED 0x00000008
/* Hardcoded pin pad reader names (PC/SC) and their pin pad lib */
static char *pp_reader_names[] = {
"Xiring X Pass Serial",
NULL
};
static char *pp_reader_libs[] = {
"xireid",
NULL
};
static BYTE aid_belpic[] = { 0xA0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35 };
static SCR_Application scr_app_belpic = {
{aid_belpic, sizeof(aid_belpic)},
"ID",
NULL
};
static char *app_id_longstr[] = {
"Identity",
"Identiteit",
"Identit<EFBFBD>",
"Identit<EFBFBD>t"
};
#endif /* BELPIC_PIN_PAD */
#if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD)
static char *pin_usg_sig[] = {
"Signature",
"Handtekening",
"Signature",
"Signatur"
};
static char *pin_usg_auth[] = {
"Authentication",
"Authentificatie",
"Authentification",
"Authentifizierung"
};
#endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */
#ifdef BELPIC_PIN_PAD
static char *lang_codes[4] = {
"en",
"nl",
"fr",
"de"
};
static char *pp_msg_auth_sh[] = {
"Authentication",
"Authentificatie",
"Authentification",
"Kennzeichnung"
};
static char *pp_msg_auth[] = {
"Enter your PIN on the reader, in order to authenticate yourself",
"Geef uw PIN in op de lezer, om u te authentificeren",
"Entrez votre PIN sur le lecteur, pour vous authentifier",
"Bitte geben Sie Ihre PIN am Kartenleseger<65>t ein, um sich zu authentifizieren"
};
static char *pp_msg_sign_sh[] = {
"Signature",
"Handtekening",
"Signature",
"Signatur"
};
static char *pp_msg_sign[] = {
"Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN on the card reader to continue or click the Cancel button.\n\nIf you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.",
"Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in op de kaartlezer om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.",
"Attention: vous allez apposer une signature <20>lectronique juridiquement valide avec votre carte d'identit<69>.\nVeuillez entrer votre PIN sur le lecteur externe pour continuer ou cliquez sur Annuler.\n\nSi vous d<>sirez seulement vous connecter <20> un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.",
"Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN am Kartenlesger<65>t ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWenn Sie nur auf das Internet gehen m<>chten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen."
};
static char *pp_msg_change_sh[] = {
"PIN change",
"PIN verandering",
"Modification du PIN ",
"PIN <20>ndern"
};
static char *pp_msg_change[] = {
"Change your PIN on the reader",
"Verander uw PIN op de lezer",
"Modifiez votre PIN sur le lecteur",
"Bitte <20>ndern Sie Ihre PIN am Kartenleseger<65>t"
};
static char *pp_msg_pin_mismatch[] = {
"The new PINs you entered were different.\n\nRetry or cancel?",
"De ingevoerde nieuwe PINs zijn verschillend.\n\nOpnieuw proberen of stoppen?",
"Les nouveaux PIN entr<74>s sont diff<66>rents.\n\nR<EFBFBD>essayer ou annuler?",
"Die von Ihnen eingegebenen PINs unterscheiden sich.\n\nErneut versuchen oder abbrechen?"
};
#define PCSC_ERROR(ctx, desc, rv) sc_debug(ctx, desc ": %lx\n", rv);
#endif /* BELPIC_PIN_PAD */
#ifdef BELPIC_SET_LANG
#define MAX_READER_LEN 100
typedef struct t_lang_info {
char reader[MAX_READER_LEN];
int lang;
} t_lang_info;
static t_lang_info lang_infos[SC_MAX_READERS];
#endif /* BELPIC_SET_LANG */
/* Language support for the GUI messages */
#ifdef HAVE_GUI
#ifdef WIN32
#define BTN_KEYB_SHORTCUT "&"
#else
#define BTN_KEYB_SHORTCUT "_"
#endif
static char *app_msg[] = {
"Identity",
"Identiteit",
#ifdef _WIN32
"Identit<EFBFBD>",
#else
"Identite",
#endif
#ifdef _WIN32
"Identit<EFBFBD>t"
#else
"Identitat",
#endif
};
static char *btn_msg_retry[4] = {
BTN_KEYB_SHORTCUT"Try again",
BTN_KEYB_SHORTCUT"Opnieuw proberen",
BTN_KEYB_SHORTCUT"R<EFBFBD>essayer",
BTN_KEYB_SHORTCUT"Erneut versuchen"
};
static char *btn_msg_cancel[4] = {
BTN_KEYB_SHORTCUT"Cancel",
BTN_KEYB_SHORTCUT"Stoppen",
BTN_KEYB_SHORTCUT"Annuler",
BTN_KEYB_SHORTCUT"Abbrechen"
};
static char *btn_msg_ok[4] = {
BTN_KEYB_SHORTCUT"OK",
BTN_KEYB_SHORTCUT"OK",
BTN_KEYB_SHORTCUT"OK",
BTN_KEYB_SHORTCUT"OK"
};
static char *btn_msg_close[4] = {
BTN_KEYB_SHORTCUT"Close",
BTN_KEYB_SHORTCUT"Sluiten",
BTN_KEYB_SHORTCUT"Fermer",
BTN_KEYB_SHORTCUT"Schliessen"
};
static char *enter_pin_msg_auth[] = {
"Enter your PIN, in order to authenticate yourself",
"Geef uw PIN in, om u te authentificeren",
"Entrez votre PIN, pour vous authentifier",
"Bitte geben Sie Ihre PIN ein, um sich zu authentifizieren"
};
static char *enter_pin_msg_sign[4] = {
#ifdef _WIN32
"Caution: You are about to make a legally binding electronic signature with your identity card.\nPlease enter your PIN to continue or click the Cancel button.\n\nWarning: if you only want to log on to a web site or server, do NOT enter your PIN and click the Cancel button.",
"Let op: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nWaarschuwing: als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.",
"Attention: vous allez apposer une signature <20>lectronique juridiquement valide avec votre carte d'identit<69>.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nPr<EFBFBD>caution: si vous d<>sirez seulement vous connecter <20> un site ou un serveur, n'entrez PAS votre PIN et cliquez sur Annuler.",
"Achtung: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen.\nBitte geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\n\nWarnung: Wenn Sie nur auf das Internet gehen m<>chten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen."
#else
#ifdef __APPLE__
"CAUTION: you are about to make a legally binding electronic signature with your identity card. Please enter your PIN to continue or press the Cancel button. If you only want to log on to a web site or a server, do NOT enter your PIN and press the Cancel button.",
"LET OP: u gaat een wettelijk bindende electronische handtekening plaatsen met uw identiteitskaart. Geef uw PIN in om verder te gaan of klik op Stoppen. Als u enkel wil aanloggen op een web site of een server, geef uw PIN NIET in en klik op Stoppen.",
"ATTENTION: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.Veuillez entrer votre PIN pour continuer ou cliquez sur Annuler. Si vous desirez seulement vous connecter a un site ou un serveur, n' entrez PAS votre PIN et cliquez sur Annuler.",
"ACHTUNG: Mit Ihrem Personalausweis werden Sie eine rechtlich bindende elektronische Signatur setzen. Geben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen. Warnung: Wenn Sie nur auf das Internet gehen mochten, geben Sie bitte Ihre PIN NICHT ein, sondern klicken Sie auf Abbrechen."
#else
"<u>Caution</u>: you are about to make a legally binding electronic\nsignature with your identity card.\nPlease enter your PIN to continue or press the Cancel button.\n\nIf you only want to log on to a web site or a server,\ndo <b>NOT</b> enter your PIN and press the Cancel button.",
"<u>Let op</u>: u gaat een wettelijk bindende electronische handtekening\nplaatsen met uw identiteitskaart.\nGeef uw PIN in om verder te gaan of klik op Stoppen.\n\nAls u enkel wil aanloggen op een web site\nof een server, geef uw PIN <b>NIET</b> in en klik op Stoppen.",
"<u>Attention</u>: vous allez apposer une signature electronique\njuridiquement valide avec votre carte d'identite.\nVeuillez entrer votre PIN pour continuer ou cliquez sur Annuler.\n\nSi vous desirez seulement vous connecter a un site\nou un serveur, n'entrez <b>PAS</b> votre PIN et cliquez sur Annuler.",
"<u>Achtung</u>: Mit Ihrem Personalausweis werden Sie eine rechtlich\r\nbindende elektronische Signatur setzen.\r\nGeben Sie Ihre PIN ein zum weitergehen oder klicken Sie auf Abbrechen.\r\n\r\nWarnung: Wenn Sie nur auf das Internet gehen mochten, geben\r\nSie bitte Ihre PIN <b>NICHT</b> ein, sondern klicken Sie auf Abbrechen."
#endif
#endif
};
static char *wrong_pin_len_msgs[4] = {
"Wrong PIN length",
"Foute PIN lengte",
"Longueur de PIN erron<6F>",
"Falsche PIN-L<>nge"
};
static char *wrong_pin_msgs[4] = {
"Wrong PIN, %d tries left\n\nRetry or cancel?",
"Foute PIN, nog %d pogingen\n\nOpnieuw proberen of stoppen?",
"PIN erron<6F>, %d essais restants\n\nR<EFBFBD>essayer ou annuler?",
"Falsche PIN, %d verbleibende Versuche\n\nErneut versuchen oder abbrechen?"
};
static char *pin_blocked_msgs[4] = {
"PIN blocked",
"PIN geblokkeerd",
"PIN bloqu<71> ",
"PIN gesperrt"
};
#endif /* HAVE_GUI */
/* To be removed */
#if 0
static void dumphex(char *msg, const u8 * buf, int len)
{
int i;
printf("%s", msg);
for (i = 0; i < len; i++)
printf("%02X ", buf[i]);
printf(" (%d bytes)\n", len);
}
#endif
#ifdef BELPIC_PIN_PAD
#define SCR_INIT_ID 100
#define SCR_VERIFY_ID 101
#define SCR_CHANGE_ID 102
#define SCR_CARD_HANDLE 999
struct tTLV {
unsigned char *base;
unsigned char *end;
unsigned char *current;
unsigned char *next;
};
static void TLVInit(struct tTLV *tlv, u8 * base, size_t size)
{
tlv->base = base;
tlv->end = base + size;
tlv->current = tlv->next = base;
}
static void TLVNext(struct tTLV *tlv, u8 tag)
{
assert(tlv->next + 2 < tlv->end);
tlv->current = tlv->next;
*(tlv->next++) = tag;
*(tlv->next++) = 0;
}
static void TLVAdd(struct tTLV *tlv, u8 val)
{
assert(tlv->next + 1 < tlv->end);
*(tlv->next++) = val;
tlv->current[1]++;
}
static void TLVAddBuffer(struct tTLV *tlv, u8 * val, size_t size)
{
assert(tlv->next + size < tlv->end);
memcpy(tlv->next, val, size);
tlv->current[1] = size;
tlv->next = tlv->next + size;
}
static size_t TLVLen(struct tTLV *tlv)
{
return tlv->next - tlv->base;
}
static LONG SCR_SCardInit(LPCTSTR szPinPadDll, LPCTSTR szReader, DWORD version,
SCR_SupportConstants * supported)
{
LONG rv;
unsigned char sendbuf[256];
unsigned char recvbuf[2];
char szTemp[32];
DWORD dwRecvLength;
struct tTLV tlv;
memset(szTemp, 0, sizeof(szTemp));
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
dwRecvLength = sizeof(recvbuf);
/* Make TLV buffer */
TLVInit(&tlv, sendbuf, sizeof(sendbuf));
TLVNext(&tlv, 0x01); /* Function ID */
sprintf(szTemp, "%ld", SCR_INIT_ID);
TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp));
TLVNext(&tlv, 0x02); /* PinPad Dll */
TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll));
TLVNext(&tlv, 0x03); /* Reader Name */
TLVAddBuffer(&tlv, (u8 *) szReader, strlen(szReader));
TLVNext(&tlv, 0x04); /* Version */
sprintf(szTemp, "%ld", version);
TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp));
#ifdef HAVE_PCSC_OLD
rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength);
#else
rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv),
recvbuf, dwRecvLength, &dwRecvLength);
#endif
if (dwRecvLength > 0) {
*supported = recvbuf[0];
} else {
rv = SC_ERROR_UNKNOWN_DATA_RECEIVED;
}
return rv;
}
static LONG SCR_SCardPIN(long lAction, LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID,
const SCR_PinUsage * pUsage, const SCR_Application * pApp,
BYTE * pCardStatus)
{
LONG rv;
unsigned char sendbuf[256];
unsigned char recvbuf[2];
char szTemp[32];
DWORD dwRecvLength;
struct tTLV tlv;
memset(szTemp, 0, sizeof(szTemp));
memset(recvbuf, 0, sizeof(recvbuf));
dwRecvLength = sizeof(recvbuf);
/* Make TLV buffer */
TLVInit(&tlv, sendbuf, sizeof(sendbuf));
TLVNext(&tlv, 0x01); /* Function ID */
sprintf(szTemp, "%ld", lAction);
TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp));
TLVNext(&tlv, 0x02); /* PinPad Dll */
TLVAddBuffer(&tlv, (u8 *) szPinPadDll, strlen(szPinPadDll));
TLVNext(&tlv, 0x03); /* SCR_Card Handle */
sprintf(szTemp, "%ld", pCard->hCard);
TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp));
if (pCard->language != NULL) {
TLVNext(&tlv, 0x04); /* SCR_Card language */
TLVAddBuffer(&tlv, (u8 *) pCard->language, strlen(pCard->language));
}
if (pCard->id.data != NULL) {
TLVNext(&tlv, 0x05); /* SCR_Card id */
TLVAddBuffer(&tlv, pCard->id.data, pCard->id.length);
}
TLVNext(&tlv, 0x06); /* PinID */
TLVAdd(&tlv, pinID);
if (pUsage != NULL) {
TLVNext(&tlv, 0x07); /* SCR_PinUsage code */
sprintf(szTemp, "%ld", pUsage->code);
TLVAddBuffer(&tlv, (u8 *) szTemp, strlen(szTemp));
if (pUsage->shortString != NULL) {
TLVNext(&tlv, 0x08); /* SCR_PinUsage shortstring */
TLVAddBuffer(&tlv, (u8 *) pUsage->shortString, strlen(pUsage->shortString));
}
if (pUsage->longString != NULL) {
TLVNext(&tlv, 0x09); /* SCR_PinUsage longstring */
TLVAddBuffer(&tlv, (u8 *) pUsage->longString, strlen(pUsage->longString));
}
}
if (pApp->id.data != NULL) {
TLVNext(&tlv, 0x0A); /* SCR_Application id */
TLVAddBuffer(&tlv, (u8 *) pApp->id.data, pApp->id.length);
}
if (pApp->shortString != NULL) {
TLVNext(&tlv, 0x0B); /* SCR_Application shortstring */
TLVAddBuffer(&tlv, (u8 *) pApp->shortString, strlen(pApp->shortString));
}
if (pApp->longString != NULL) {
TLVNext(&tlv, 0x0C); /* SCR_Application longstring */
TLVAddBuffer(&tlv, (u8 *) pApp->longString, strlen(pApp->longString));
}
#ifdef HAVE_PCSC_OLD
rv = SCardControl(SCR_CARD_HANDLE, sendbuf, TLVLen(&tlv), recvbuf, &dwRecvLength);
#else
rv = SCardControl(SCR_CARD_HANDLE, 0, sendbuf, TLVLen(&tlv),
recvbuf, dwRecvLength, &dwRecvLength);
#endif
if (dwRecvLength < 2) {
rv = SC_ERROR_UNKNOWN_DATA_RECEIVED;
} else {
memcpy(pCardStatus, recvbuf, 2);
}
return rv;
}
static LONG SCR_SCardVerifyPIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID,
const SCR_PinUsage * pUsage, const SCR_Application * pApp,
BYTE * pCardStatus)
{
return SCR_SCardPIN(SCR_VERIFY_ID, szPinPadDll, pCard, pinID, pUsage, pApp, pCardStatus);
}
static LONG SCR_SCardChangePIN(LPCTSTR szPinPadDll, const SCR_Card * pCard, BYTE pinID,
const SCR_Application * pApp, BYTE * pCardStatus)
{
return SCR_SCardPIN(SCR_CHANGE_ID, szPinPadDll, pCard, pinID, NULL, pApp, pCardStatus);
}
#endif /* BELPIC_PIN_PAD */
#if defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD)
static int belpic_calculate_lang(sc_card_t *card)
{
struct belpic_priv_data *priv = DRVDATA(card);
int lang = priv->lang;
#ifdef BELPIC_SET_LANG
int i;
for (i = 0; i < SC_MAX_READERS; i++) {
if (lang_infos[i].reader[0] == '\0') {
if (lang_infos[i].lang != LNG_NONE)
lang = lang_infos[i].lang;
break;
}
if (strncmp(lang_infos[i].reader, card->reader->name, MAX_READER_LEN) == 0) {
if (lang_infos[i].lang != LNG_NONE)
lang = lang_infos[i].lang;
}
}
#endif /* BELPIC_SET_LANG */
return lang;
}
#endif /* defined(HAVE_GUI) ||defined(BELPIC_PIN_PAD) */
#ifdef BELPIC_SET_LANG
/**
* Force the language for the GUI and pinpad readers for one specific
* or for all readers.
* - IN reader: the PC/SC name of the reader, or NULL for all readers
* - IN lang: 0 for English, 1 for Dutch, 2 for French, 3 for German
* and 0xFFFF to clear a previously selected language.
* Returns:
* 0 if OK,
* -1 if a bad language code was given,
* -2 if you called this function with more then MAX_READER_LEN (16)
* different reader names and a lang code different from 0xFF
*/
int belpic_set_language(const char *reader, int lang)
{
int i;
/* Check if language has a correct value */
if ((lang != LNG_NONE) && (lang < LNG_ENG || lang > LNG_GERMAN))
return -1; /* Bad language */
/* Set or clear the language for the/all reader(s) */
for (i = 0; i < SC_MAX_READERS; i++) {
if (reader == NULL) { /* For all readers */
lang_infos[i].lang = lang;
if (lang == LNG_NONE)
lang_infos[i].reader[0] = '\0';
} else { /* For only 1 reader */
if (lang_infos[i].reader[0] == '\0') { /* reader not yet present */
strlcpy(lang_infos[i].reader, reader, sizeof(lang_infos[i].reader));
lang_infos[i].lang = lang;
break;
} else if (strncmp(reader, lang_infos[i].reader, MAX_READER_LEN - 1) == 0) {
lang_infos[i].lang = lang;
break;
} else if (i == SC_MAX_READERS - 1)
return -2; /* Too many readers (shouldn't happen) */
}
}
return 0;
}
#endif /* BELPIC_SET_LANG */
static int str2lang(sc_context_t *ctx, char *lang)
{
if (memcmp(lang, "en", 2) == 0)
return LNG_ENG;
else if (memcmp(lang, "nl", 2) == 0)
return LNG_DUTCH;
else if (memcmp(lang, "fr", 2) == 0)
return LNG_FRENCH;
else if (memcmp(lang, "de", 2) == 0)
return LNG_GERMAN;
sc_debug(ctx, "Unknown/unsupported language code: %c%c\n", lang[0], lang[1]);
return -1;
}
#ifdef GET_LANG_FROM_CARD
/* str is in lower case, the case of buf can be both, and buf is large enough */
static int match_string(const char *str, const char *buf)
{
int i = 0;
while (str[i] != '\0') {
if (str[i] != ((buf[i] >= 'A' && buf[i] <= 'Z') ? buf[i] + 32 : buf[i]))
return 0;
i++;
}
return 1; /* match */
}
static int get_pref(const char *prefs, int prefs_len, const char *title, const char *key, int *len)
{
int i = 0;
int title_len = strlen(title);
int key_len = strlen(key);
while (prefs[i] != '\0' && i < prefs_len)
i++;
prefs_len = i;
i = 0;
while (i < prefs_len) {
while (i < prefs_len && prefs[i] != '[')
i++;
if (i + title_len >= prefs_len)
return -1;
if (!match_string(title, prefs + i)) {
i++;
continue;
}
i += title_len;
while (i < prefs_len) {
while (i < prefs_len && (prefs[i] == '\r' || prefs[i] == '\n'))
i++;
if (i < prefs_len && prefs[i] == '[')
break;
if (i + key_len + 1 >= prefs_len)
return -2;
if (!match_string(key, prefs + i)) {
i++;
continue;
}
i += key_len;
if (prefs[i] != '=')
return -3;
*len = ++i;
while (*len < prefs_len && prefs[*len] != '\r' && prefs[*len] != '\n')
(*len)++;
*len -= i;
return i;
}
}
return -1;
}
static int get_language(sc_card_t *card)
{
sc_apdu_t apdu;
u8 prefs[240], *lg_value;
u8 path[] = { 0x3F, 0x00, 0xDF, 0x01, 0x40, 0x39 };
int r, i, len;
/* Get the language from the card's preferences file */
assert(card != NULL);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C);
apdu.lc = sizeof(path);
apdu.data = path;
apdu.datalen = sizeof(path);
apdu.resplen = 0;
apdu.le = 0;
r = sc_lock(card);
if (r < 0)
goto prefs_error;
r = sc_transmit_apdu(card, &apdu);
if (r < 0) {
sc_debug(card->ctx, "Select_File[prefs_file] command failed: %d\n", r);
sc_unlock(card);
goto prefs_error;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r < 0) {
sc_debug(card->ctx, "Select_File[prefs_file]: card returned %d\n", r);
sc_unlock(card);
goto prefs_error;
}
r = iso_ops->read_binary(card, 0, prefs, sizeof(prefs), 0);
sc_unlock(card);
if (r <= 0) {
sc_debug(card->ctx, "Read_Binary[prefs_file] returned %d\n", r);
goto prefs_error;
}
#if 0
dumphex("Prefs: ", prefs, r);
#endif
i = get_pref(prefs, r, "[gen]", "lg", &len);
if (i <= 0 || len < 2) {
sc_debug(card->ctx, "Couldn't find language in prefs file: %d\n", i);
goto prefs_error;
}
lg_value = prefs + i; /* language code(s) found, starts here */
i = 0;
while (1) {
while (i <= len - 2 && (lg_value[i] == ' ' || lg_value[i] == '|'))
i++;
if (i > len - 2)
goto prefs_error;
r = str2lang(card->ctx, lg_value + i);
if (r >= 0)
return r;
i += 2;
}
prefs_error:
/* If troubles with the card's prefs file, get the language from the OS */
#ifdef _WIN32
switch (GetUserDefaultLangID() & 0x00FF) {
case 0x13:
return LNG_DUTCH;
case 0x0C:
return LNG_FRENCH;
case 0x07:
return LNG_GERMAN;
default:
return LNG_ENG;
}
#endif
return LNG_ENG; /* default */
}
#endif /* GET_LANG_FROM_CARD */
static scconf_block *get_belpic_conf(sc_context_t *ctx, const char *name)
{
scconf_block *conf_block = NULL, **blocks;
int i;
for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name, NULL);
if (!blocks)
return NULL;
conf_block = blocks[0];
free(blocks);
if (conf_block != NULL)
break;
}
return conf_block;
}
#ifdef BELPIC_PIN_PAD
static void load_pin_pad_err(const char *reader_name, const char *pp_reader_lib, char *msg)
{
char buf[300];
void *hDlg;
if (strlen(reader_name) + strlen(pp_reader_lib) > 200)
return;
sprintf(buf, "Error while loading library \"%s\" for pin pad reader \"%s\": %s\n",
pp_reader_lib, reader_name, msg);
scgui_ask_message(app_msg[0], "Pin pad library error", buf, btn_msg_close[0], NULL,
reader_name);
}
static int belpic_load_pin_pad_lib(sc_card_t *card, struct belpic_priv_data *priv_data,
const char *reader_name, const char *pp_reader_lib)
{
LONG r;
DWORD supported;
memset(priv_data->szPinPadDll, 0, sizeof(priv_data->szPinPadDll));
strcpy(priv_data->szPinPadDll, pp_reader_lib);
priv_data->scr_init = (FARPROC) SCR_SCardInit;
priv_data->scr_verify_pin = (FARPROC) SCR_SCardVerifyPIN;
priv_data->scr_change_pin = (FARPROC) SCR_SCardChangePIN;
if (priv_data->scr_init == NULL || priv_data->scr_verify_pin == NULL) {
sc_debug(card->ctx, "Function not found in \"%s\" err = 0x%0x\n",
pp_reader_lib, GetLastError());
load_pin_pad_err(reader_name, pp_reader_lib,
"unsufficient functionality found in library");
return SC_ERROR_READER;
}
r = priv_data->scr_init(pp_reader_lib, reader_name, 1, &supported);
if (r != SCARD_S_SUCCESS) {
sc_debug(card->ctx, "SCR_Init() returned 0x%0x\n", r);
load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library failed");
return SC_ERROR_READER;
}
if (supported) {
sc_debug(card->ctx, "SCR_init() returned not supported code 0x%0x\n", supported);
load_pin_pad_err(reader_name, pp_reader_lib,
"Initialization of library returned UNSUPPORTED");
return SC_ERROR_READER;
}
#if 0
HINSTANCE dll = LoadLibrary(pp_reader_lib);
sc_debug(card->ctx, "Pin pad reader \"%s\" found, now loading corresponding lib \"%s\"\n",
reader_name, pp_reader_lib);
if (dll == NULL) {
sc_debug(card->ctx, "Unable to load library \"%s\", err = 0x%0x\n",
pp_reader_lib, GetLastError());
load_pin_pad_err(reader_name, pp_reader_lib,
"library not found or unable to load it");
return SC_ERROR_READER;
}
priv_data->scr_init = GetProcAddress(dll, "SCR_Init");
priv_data->scr_verify_pin = GetProcAddress(dll, "SCR_VerifyPIN");
priv_data->scr_change_pin = GetProcAddress(dll, "SCR_ChangePIN");
if (priv_data->scr_init == NULL || priv_data->scr_verify_pin == NULL) {
sc_debug(card->ctx, "Function not found in \"%s\" err = 0x%0x\n",
pp_reader_lib, GetLastError());
load_pin_pad_err(reader_name, pp_reader_lib,
"unsufficient functionality found in library");
return SC_ERROR_READER;
}
r = priv_data->scr_init(reader_name, 1, &supported);
if (r != SCARD_S_SUCCESS) {
sc_debug(card->ctx, "SCR_Init() returned 0x%0x\n", r);
load_pin_pad_err(reader_name, pp_reader_lib, "Initialization of library failed");
return SC_ERROR_READER;
}
if (supported) {
sc_debug(card->ctx, "SCR_init() returned not supported code 0x%0x\n", supported);
load_pin_pad_err(reader_name, pp_reader_lib,
"Initialization of library returned UNSUPPORTED");
return SC_ERROR_READER;
}
#endif
return 1;
}
static int belpic_detect_pin_pad(sc_card_t *card, struct belpic_priv_data *priv_data)
{
int i = 0;
char *reader_name = card->reader->name, *conf_reader, *conf_lib;
scconf_block *conf_block = NULL;
char *reader_i = "reader ", *lib_i = "lib ";
int rn_len = strlen(reader_name);
/* Hardcoded readers */
for (i = 0; pp_reader_names[i] != NULL; i++) {
int pp_rn_len = strlen(pp_reader_names[i]);
if (rn_len >= pp_rn_len && strncmp(reader_name, pp_reader_names[i], pp_rn_len) == 0) {
return belpic_load_pin_pad_lib(card, priv_data,
reader_name, pp_reader_libs[i]);
}
}
/* From the config file */
conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad");
if (conf_block == NULL)
return 0;
for (i = 0; i < 10; i++) {
reader_i[6] = (char) (0x30 + i);
conf_reader = (char *) scconf_get_str(conf_block, reader_i, NULL);
if (conf_reader != NULL && rn_len >= strlen(conf_reader) &&
strncmp(reader_name, conf_reader, strlen(conf_reader)) == 0) {
lib_i[3] = (char) (0x30 + i);
conf_lib = (char *) scconf_get_str(conf_block, lib_i, NULL);
if (conf_lib != NULL)
return belpic_load_pin_pad_lib(card, priv_data,
reader_name, conf_lib);
}
}
return 0;
}
#endif /* BELPIC_PIN_PAD */
static int belpic_finish(sc_card_t *card)
{
free(DRVDATA(card));
return 0;
}
static int belpic_match_card(sc_card_t *card)
{
int i;
i = _sc_match_atr(card, belpic_atrs, &card->type);
if (i < 0)
return 0;
return 1;
}
static int belpic_init(sc_card_t *card)
{
struct belpic_priv_data *priv = NULL;
scconf_block *conf_block;
#ifdef BELPIC_PIN_PAD
int r;
#endif
sc_debug(card->ctx, "Belpic V%s", BELPIC_VERSION);
#ifdef HAVE_GUI
sc_debug(card->ctx, " with GUI support");
#endif
#ifdef BELPIC_PIN_PAD
sc_debug(card->ctx, " with support for pin pad reader libs");
#endif
sc_debug(card->ctx, "\n");
if (card->type < 0)
card->type = SC_CARD_TYPE_BELPIC_EID; /* Unknown card: assume it's the Belpic Card */
priv = (struct belpic_priv_data *) calloc(1, sizeof(struct belpic_priv_data));
if (priv == NULL)
return SC_ERROR_OUT_OF_MEMORY;
card->drv_data = priv;
card->cla = 0x00;
if (card->type == SC_CARD_TYPE_BELPIC_EID) {
_sc_card_add_rsa_alg(card, 1024,
SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE, 0);
}
/* V1 applets have a problem: if the card sends a 6C XX (only XX bytes available),
* and we resend the command too soon (i.e. the reader is too fast), the card
* doesn't respond. So we build in a delay. */
card->wait_resend_apdu = 40;
/* State that we have an RNG */
card->caps |= SC_CARD_CAP_RNG;
/* State that we don't return FCI (no file type, no file size, ...) */
card->caps |= SC_CARD_CAP_NO_FCI;
/* Language prefences */
priv->lang = -1;
conf_block = get_belpic_conf(card->ctx, "belpic_general");
if (conf_block != NULL) {
char *lang = (char *) scconf_get_str(conf_block, "force_language", NULL);
if (lang != NULL && strlen(lang) == 2)
priv->lang = str2lang(card->ctx, lang);
}
#ifdef GET_LANG_FROM_CARD
if (priv->lang == -1)
priv->lang = get_language(card);
#endif
card->max_pin_len = BELPIC_MAX_USER_PIN_LEN;
#ifdef HAVE_GUI
r = scgui_init();
if (r != 0)
sc_debug(card->ctx, "scgui_init() returned error %d\n", i);
#endif
#ifdef BELPIC_PIN_PAD
r = belpic_detect_pin_pad(card, priv);
if (r == 1)
card->reader->capabilities |= SC_READER_CAP_PIN_PAD;
else if (r < 0)
return r; /* error loading/initing pin pad lib */
conf_block = get_belpic_conf(card->ctx, "belpic_pin_pad");
if (conf_block != NULL) {
if (scconf_get_bool(conf_block, "msg_auth_pin", 1))
priv->options |= PP_MSG_AUTH_PIN;
if (scconf_get_bool(conf_block, "msg_wrong_pin", 1))
priv->options |= PP_MSG_WRONG_PIN;
if (scconf_get_bool(conf_block, "msg_changepin_mismatch", 1))
priv->options |= PP_MSG_CHANGEPIN_MISMATCH;
if (scconf_get_bool(conf_block, "msg_pin_blocked", 1))
priv->options |= PP_MSG_PIN_BLOCKED;
}
#endif
return 0;
}
static int belpic_select_file(sc_card_t *card,
const sc_path_t *in_path, sc_file_t **file_out)
{
sc_context_t *ctx;
sc_apdu_t apdu;
u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
int r, pathlen;
sc_file_t *file = NULL;
assert(card != NULL && in_path != NULL);
ctx = card->ctx;
memcpy(path, in_path->value, in_path->len);
pathlen = in_path->len;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C);
apdu.lc = pathlen;
apdu.data = path;
apdu.datalen = pathlen;
apdu.resplen = 0;
apdu.le = 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "Select File APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (r)
SC_FUNC_RETURN(card->ctx, 2, r);
next_idx = (size_t)-1; /* reset */
if (file_out != NULL) {
file = sc_file_new();
file->path = *in_path;
if (pathlen >= 2)
file->id = (in_path->value[pathlen - 2] << 8) | in_path->value[pathlen - 1];
file->size = BELPIC_MAX_FILE_SIZE;
file->shareable = 1;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
if (pathlen == 2 && memcmp("\x3F\x00", in_path->value, 2) == 0)
file->type = SC_FILE_TYPE_DF;
else
file->type = SC_FILE_TYPE_WORKING_EF;
*file_out = file;
}
return 0;
}
static int belpic_read_binary(sc_card_t *card,
unsigned int idx, u8 * buf, size_t count, unsigned long flags)
{
int r;
if (next_idx == idx)
return 0; /* File was already read entirely */
t1 = clock();
r = iso_ops->read_binary(card, idx, buf, count, flags);
t2 = clock();
/* If the 'next_idx trick' shouldn't work, we hope this error
* means that an attempt was made to read beyond the file's
* contents, so we'll return 0 to end the loop in sc_read_binary()*/
if (r == SC_ERROR_INCORRECT_PARAMETERS)
return 0;
if (r >= 0 && (size_t)r < count)
next_idx = idx + (size_t)r;
dur = t2 - t1;
tot_dur += dur;
tot_read += r;
#if 0
printf("%d bytes: %d ms - %d bytes total: %d ms\n", r, dur, tot_read, tot_dur);
#endif
return r;
}
#ifdef BELPIC_PIN_PAD
/* Test the result code of the pin pad reader + the card's status bytes */
static int belpic_pp_test_res(sc_card_t *card, int r, const u8 * card_status, int *tries_left)
{
#if 0
printf("PP res: 0x%0x (%d), SW1-SW2 = %02x %02x\n", r, r, card_status[0], card_status[1]);
#endif
if (r != SCARD_S_SUCCESS) {
switch (r) {
case SCARD_E_CANCELLED:
return SC_ERROR_KEYPAD_CANCELLED;
case SCARD_W_REMOVED_CARD:
return SC_ERROR_CARD_REMOVED;
case SCR_I_PIN_CHECK_FAILED:
return SC_ERROR_KEYPAD_PIN_MISMATCH;
default:
return SC_ERROR_TRANSMIT_FAILED;
}
}
if (card_status[0] == 0xEC && card_status[1] == 0xD2)
return SC_ERROR_KEYPAD_TIMEOUT;
if (card_status[0] == 0xEC && card_status[1] == 0xD6)
return SC_ERROR_KEYPAD_CANCELLED;
if (card_status[0] == 0x63) {
if ((card_status[1] & 0xF0) == 0xC0 && tries_left != NULL)
*tries_left = card_status[1] & 0x0F;
return SC_ERROR_PIN_CODE_INCORRECT;
}
return sc_check_sw(card, card_status[0], card_status[1]);
}
/* Send the verify pin command to the pin pad reader + optionally show message */
static int belpic_pp_verify(sc_card_t *card, SCR_Card * scr_card,
struct belpic_priv_data *priv, int pin_ref,
int pin_usage, int *tries_left)
{
BYTE card_status[2];
void *hDlg;
int first_time = 1, r = SC_ERROR_PIN_CODE_INCORRECT;
int lang = belpic_calculate_lang(card);
SCR_PinUsage scr_pin_usage = {
pin_usage,
pin_usage == SCR_USAGE_SIGN ? "SIG" : "AUT",
pin_usage == SCR_USAGE_SIGN ? pin_usg_sig[lang] : pin_usg_auth[lang]
};
char *reader_name = card->reader->name;
char *pp_msg_login_sh =
(pin_usage == SCR_USAGE_SIGN ? pp_msg_sign_sh[lang] : pp_msg_auth_sh[lang]);
char *pp_msg_login = (pin_usage == SCR_USAGE_SIGN ? pp_msg_sign[lang] : pp_msg_auth[lang]);
scgui_param_t icon = (pin_usage == SCR_USAGE_SIGN ? SCGUI_SIGN_ICON : SCGUI_NO_ICON);
int mesg_on_screen = (priv->options & PP_MSG_AUTH_PIN) ||
(pin_usage != SCR_USAGE_AUTH) || SSO_OK(card->ctx);
while (r == SC_ERROR_PIN_CODE_INCORRECT) {
if (!first_time) {
if (priv->options & PP_MSG_WRONG_PIN) {
int r1;
char msg[200];
sprintf(msg, wrong_pin_msgs[lang], *tries_left);
r1 = scgui_ask_message(app_msg[lang], pp_msg_login_sh, msg,
btn_msg_retry[lang], btn_msg_cancel[lang],
reader_name);
if (r1 == SCGUI_CANCEL)
return r;
else if (r1 != SCGUI_OK) {
sc_debug(card->ctx, "scgui_ask_message returned %d\n", r1);
return SC_ERROR_INTERNAL;
}
} else
return r;
}
first_time = 0;
if (mesg_on_screen) {
scgui_display_message(app_msg[lang], pp_msg_login_sh, pp_msg_login,
NULL, &hDlg, icon, reader_name);
}
#if 0
printf("belpic_pp_verify(): reader=%s, hCard=0x%0x\n", card->reader->name,
scr_card->hCard);
#endif
r = priv->scr_verify_pin(priv->szPinPadDll, scr_card, pin_ref,
&scr_pin_usage, &scr_app_belpic, card_status);
if (mesg_on_screen)
scgui_remove_message(hDlg);
sc_debug(card->ctx, "SCR_Verify_PIN(): res = 0x%0x, status = %2X %2X\n",
r, card_status[0], card_status[1]);
r = belpic_pp_test_res(card, r, card_status, tries_left);
}
return r;
}
/* Send the change pin command to the pin pad reader + show message */
static int belpic_pp_change(sc_card_t *card, SCR_Card * scr_card,
struct belpic_priv_data *priv, int pin_ref, int *tries_left)
{
BYTE card_status[2];
void *hDlg;
int first_time = 1, r = SC_ERROR_KEYPAD_PIN_MISMATCH, r1;
int lang = belpic_calculate_lang(card);
char *reader_name = card->reader->name;
while (r == SC_ERROR_KEYPAD_PIN_MISMATCH || r == SC_ERROR_PIN_CODE_INCORRECT) {
if (!first_time) {
int r1 = SCGUI_OK;
if (r == SC_ERROR_KEYPAD_PIN_MISMATCH) {
if (!(priv->options & PP_MSG_CHANGEPIN_MISMATCH))
return r;
r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang],
pp_msg_pin_mismatch[lang],
btn_msg_retry[lang], btn_msg_cancel[lang],
reader_name);
}
if (r == SC_ERROR_PIN_CODE_INCORRECT) {
char msg[200];
if (!(priv->options & PP_MSG_WRONG_PIN))
return r;
sprintf(msg, wrong_pin_msgs[lang], *tries_left);
r1 = scgui_ask_message(app_msg[lang], pp_msg_change_sh[lang],
msg, btn_msg_retry[lang],
btn_msg_cancel[lang], reader_name);
}
if (r1 == SCGUI_CANCEL)
return r;
else if (r1 != SCGUI_OK) {
sc_debug(card->ctx, "scgui_ask_message returned %d\n", r1);
return SC_ERROR_INTERNAL;
}
}
first_time = 0;
scgui_display_message(app_msg[lang], pp_msg_change_sh[lang],
pp_msg_change[lang], NULL, &hDlg, SCGUI_NO_ICON, reader_name);
r = priv->scr_change_pin(priv->szPinPadDll, scr_card, pin_ref,
&scr_app_belpic, card_status);
scgui_remove_message(hDlg);
sc_debug(card->ctx, "SCR_Change_PIN(): res = 0x%0x, status = %2X %2X\n",
r, card_status[0], card_status[1]);
r = belpic_pp_test_res(card, r, card_status, tries_left);
}
return r;
}
#endif /* BELPIC_PIN_PAD */
static int belpic_pin_cmd_usage(sc_card_t *card, struct sc_pin_cmd_data *data,
int *tries_left, int pin_usage)
{
#ifdef BELPIC_PIN_PAD
sc_apdu_t apdu;
int r;
struct belpic_priv_data *priv = DRVDATA(card);
int lang = belpic_calculate_lang(card);
if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) {
LONG r;
SCR_Card scr_card = {
priv->pcsc_card,
lang_codes[lang],
{NULL, 0}
,
NULL
};
scr_app_belpic.longString = app_id_longstr[lang];
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
r = belpic_pp_verify(card, &scr_card,
priv, data->pin_reference, pin_usage, tries_left);
break;
case SC_PIN_CMD_CHANGE:
r = belpic_pp_change(card, &scr_card,
priv, data->pin_reference, tries_left);
break;
default:
r = SC_ERROR_NOT_SUPPORTED;
}
if (r == SC_ERROR_AUTH_METHOD_BLOCKED && (priv->options & PP_MSG_PIN_BLOCKED))
scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang],
btn_msg_close[lang], NULL, card->reader->name);
return r;
}
#endif /* BELPIC_PIN_PAD */
data->pin1.encoding = data->pin2.encoding = BELPIC_PIN_ENCODING;
data->pin1.pad_char = data->pin2.pad_char = BELPIC_PAD_CHAR;
data->pin1.min_length = data->pin2.min_length = BELPIC_MIN_USER_PIN_LEN;
data->pin1.max_length = data->pin1.max_length = BELPIC_MAX_USER_PIN_LEN;
data->apdu = NULL;
return iso_ops->pin_cmd(card, data, tries_left);
}
static int belpic_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
if (SSO_OK(card->ctx) && data->cmd == SC_PIN_CMD_VERIFY)
return 0; /* Don't log in right now, just say it's OK */
else
return belpic_pin_cmd_usage(card, data, tries_left, 1); /* SCR_USAGE_AUTH = 1 */
}
#ifdef HAVE_GUI
/* Called by belpic_set_security_env() when a NonRep signature will be done,
* or by belpic-compute_signature the first fime an auth signature is done
* and the allow_sso is true
*/
static int belpic_askpin_verify(sc_card_t *card, int pin_usage)
{
struct sc_pin_cmd_data data;
sc_apdu_t apdu;
u8 pin_data[BELPIC_MAX_USER_PIN_LEN + 1];
int pin_len;
int tries_left;
int r;
struct belpic_priv_data *priv = DRVDATA(card);
int lang = belpic_calculate_lang(card);
char *enter_pin_msg = (pin_usage == SCR_USAGE_AUTH ?
enter_pin_msg_auth[lang] : enter_pin_msg_sign[lang]);
scgui_param_t icon = (pin_usage == SCR_USAGE_AUTH ? SCGUI_NO_ICON : SCGUI_SIGN_ICON);
data.pin1.encoding = BELPIC_PIN_ENCODING;
data.pin1.pad_char = BELPIC_PAD_CHAR;
data.pin1.min_length = BELPIC_MIN_USER_PIN_LEN;
data.pin1.max_length = BELPIC_MAX_USER_PIN_LEN;
data.cmd = SC_PIN_CMD_VERIFY;
data.flags = 0;
data.pin_type = SC_AC_CHV;
data.pin_reference = 1;
#ifdef BELPIC_PIN_PAD
/* In case of a pinpad reader */
if (card->reader->capabilities & SC_READER_CAP_PIN_PAD && priv->scr_init != NULL) {
data.pin1.data = NULL;
data.pin1.len = 0;
return belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage);
}
#endif
pin_len = BELPIC_MAX_USER_PIN_LEN + 1;
r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len,
btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang], icon);
if (r == SCGUI_CANCEL)
return SC_ERROR_KEYPAD_CANCELLED;
if (r != SCGUI_OK)
return SC_ERROR_INTERNAL;
data.pin1.data = pin_data;
data.pin1.len = pin_len;
r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage);
/* card->ctx->allow_sso = true: we do PIN mgmnt ourselves */
while (r == SC_ERROR_PIN_CODE_INCORRECT && SSO_OK(card->ctx)) {
int r1;
char msg[200];
sprintf(msg, wrong_pin_msgs[lang], tries_left);
r1 = scgui_ask_message(app_msg[lang], pin_usg_auth[lang], msg,
btn_msg_retry[lang], btn_msg_cancel[lang],
card->reader->name);
if (r1 == SCGUI_CANCEL)
return r;
else if (r1 != SCGUI_OK) {
sc_debug(card->ctx, "scgui_ask_message returned %d\n", r1);
return SC_ERROR_INTERNAL;
}
pin_len = BELPIC_MAX_USER_PIN_LEN + 1;
r = scgui_enterpin(app_msg[lang], enter_pin_msg, pin_data, &pin_len,
btn_msg_ok[lang], btn_msg_cancel[lang], wrong_pin_len_msgs[lang],
icon);
if (r == SCGUI_CANCEL)
return SC_ERROR_KEYPAD_CANCELLED;
if (r != SCGUI_OK)
return SC_ERROR_INTERNAL;
data.pin1.data = pin_data;
data.pin1.len = pin_len;
r = belpic_pin_cmd_usage(card, &data, &tries_left, pin_usage);
if (tries_left == 0)
r = SC_ERROR_AUTH_METHOD_BLOCKED;
}
if (r == SC_ERROR_AUTH_METHOD_BLOCKED && SSO_OK(card->ctx))
scgui_ask_message(app_msg[lang], " ", pin_blocked_msgs[lang],
btn_msg_close[lang], NULL, card->reader->name);
return r;
}
#endif /* HAVE_GUI */
static int belpic_set_security_env(sc_card_t *card,
const sc_security_env_t *env, int se_num)
{
sc_apdu_t apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
sc_debug(card->ctx, "belpic_set_security_env(), keyRef = 0x%0x, algo = 0x%0x\n",
*env->key_ref, env->algorithm_flags);
assert(card != NULL && env != NULL);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
switch (env->operation) {
case SC_SEC_OPERATION_SIGN:
apdu.p1 = 0x41;
apdu.p2 = 0xB6;
sbuf[0] = 0x04; /* length of the following data */
sbuf[1] = 0x80; /* tag for algorithm reference */
if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
sbuf[2] = 0x01;
else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
sbuf[2] = 0x02;
else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5)
sbuf[2] = 0x04;
else {
sc_debug(card->ctx, "Set Sec Env: unsupported algo 0X%0X\n",
env->algorithm_flags);
return SC_ERROR_INVALID_ARGUMENTS;
}
sbuf[3] = 0x84; /* tag for private key reference */
sbuf[4] = *env->key_ref; /* key reference */
apdu.lc = 5;
apdu.datalen = 5;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
apdu.le = 0;
apdu.data = sbuf;
apdu.resplen = 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card's Set Security Env command returned error");
/* If a NonRep signature will be done, ask to enter a PIN. It would be more
* logical to put the code below into the compute signature function because
* a Verify Pin call must immediately preceed a Compute Signature call.
* It's not done because the Compute Signature is completely ISO7816 compliant
* so we use the iso7816_compute_signature() function, and because this function
* doesn't know about the key reference.
* It's not a problem either, because this function is (for pkcs11) only called
* by sc_pkcs15_compute_signature(), where the card is already locked, and
* the next function to be executed will be the compute_signature function.
*/
if (*env->key_ref == BELPIC_KEY_REF_NONREP) {
#ifdef HAVE_GUI
r = belpic_askpin_verify(card, SCR_USAGE_SIGN);
if (r != 0 && r != SC_ERROR_KEYPAD_CANCELLED)
sc_debug(card->ctx, "Verify PIN in SET command returned %d\n", r);
else
sc_debug(card->ctx, "Verify PIN in SET command returned %d\n", r);
#else
sc_debug(card->ctx, "No GUI for NonRep key present, signature cancelled\n");
return SC_ERROR_NOT_SUPPORTED;
#endif
}
return r;
}
static int belpic_compute_signature(sc_card_t *card, const u8 * data,
size_t data_len, u8 * out, size_t outlen)
{
int r;
r = iso_ops->compute_signature(card, data, data_len, out, outlen);
#ifdef HAVE_GUI
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) {
r = belpic_askpin_verify(card, SCR_USAGE_AUTH);
if (r == 0)
r = iso_ops->compute_signature(card, data, data_len, out, outlen);
}
#endif
return r;
}
static int belpic_update_binary(sc_card_t *card,
unsigned int idx, const u8 *buf, size_t count,
unsigned long flags)
{
int r;
r = iso_ops->update_binary(card, idx, buf, count, flags);
#ifdef HAVE_GUI
if (r == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED && SSO_OK(card->ctx)) {
r = belpic_askpin_verify(card, SCR_USAGE_AUTH);
if (r == 0)
r = iso_ops->update_binary(card, idx, buf, count, flags);
}
#endif
return r;
}
#if 0
static int belpic_logout(sc_card_t *card)
{
sc_apdu_t apdu;
int r;
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE6, 0x00, 0x00);
apdu.cla = 0x80;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "LOGOFF: APDU transmit failed");
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "LOGOFF returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
#endif
static struct sc_card_driver *sc_get_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
belpic_ops.match_card = belpic_match_card;
belpic_ops.init = belpic_init;
belpic_ops.finish = belpic_finish;
belpic_ops.update_binary = belpic_update_binary;
belpic_ops.select_file = belpic_select_file;
belpic_ops.read_binary = belpic_read_binary;
belpic_ops.pin_cmd = belpic_pin_cmd;
belpic_ops.set_security_env = belpic_set_security_env;
belpic_ops.compute_signature = belpic_compute_signature;
belpic_ops.get_challenge = iso_ops->get_challenge;
belpic_ops.get_response = iso_ops->get_response;
belpic_ops.check_sw = iso_ops->check_sw;
return &belpic_drv;
}
#if 1
struct sc_card_driver *sc_get_belpic_driver(void)
{
return sc_get_driver();
}
#endif