Implementing a pinpad plugin in the eID V3 middleware by rqy18723

VIEWS: 32 PAGES: 7

									               Pinpad readers in the eID V3 middleware

Introduction

This document explains how pinpad reader support is implemented in the middleware and how
to implement a pinpad plugin in order to support a certain pinpad reader (if this would be
needed).

PIN management

The middleware provides functionality to verify and change PINs. In these functions, specifying
the PIN value(s) is optional:
   - If a PIN is specified, it will always be used (sent to the card)
   - If no PIN is specified (or for a PIN change only the old or new PIN, but not both), the
       middleware will do the following:
          o If the reader in which the card is present is recognized as a pinpad reader, then
              the middleware will tell the pinpad reader to ask for the PIN (and then send it to
              the card)
          o In the case of no pinpad reader, the middleware will show a PIN dialog to ask for
              the PIN(s) and send it to the card.

The PIN buffers of the BE eID card are 8 bytes long and formatted according to the ‘Global
Platform’ encoding.
E.g. if PIN = “01234567891” -> PIN buffer for verification = 2B 01 23 45 67 89 1F FF
E.g. if old PIN = “1234” and new PIN = 77777 -> PIN buffer for change = 24 12 13 FF FF FF
FF FF 35 77 77 7F FF FF FF FF

Pinpad recognition

The middleware supports CCID [1] readers. So when a card is inserted in a reader and a PIN
has to be verified or changed, the middleware will call an ScardControl() PCSC function with a
CCID_IOCTL_GET_FEATURE_REQUEST control code to the reader. If the pinpad reader
responds to this function with a return code = SCARD_S_SUCCESS, and the response contains
a feature buffer that is recognized and supported, then the reader is assumed to be a CCID
pinpad reader and treated accordingly. The middleware supports the following features:
CCID_VERIFY_START/CCID_VERIFY_FINISH, CCID_VERIFY_DIRECT,
CCID_CHANGE_START/CCID_CHANGE_FINISH, CCID_CHANGE_DIRECT.

Additionally, it is also possible to create a pinpad plugin (a shared library that is dynamically
loaded by the middleware) for a specific reader, this can be for the following reasons:
    - because the pinpad reader is not (fully) CCID compliant
    - to specify which messages should be displayed on the PC by the middleware (e.g. info
       on how to use the pinpad reader)
    - to specify which messages should be displayed on the pinpad reader (since this is not
       foreseen in the CCID standard)
A pinpad plugin implements 2 functions: one ‘init’ function and one ‘command’ function with as
main parameter the CCID data that is normally sent to the reader in an ScardControl()
function. The pinpad plugin can then modify this data into the proprietory format needed for
that specific pinpad reader and sent it to that reader.

For backward compatibility, the old pinpad plugins are also still supported. These have a more
restricted interface and won’t be usable with e.g. V2 eID cards (that will have 2 PINs).

To summarize, the full pinpad-related behavior is the following:
   1. If a pinpad plugin is found for that reader, all pin related commands (in CCID format)
      are sent to this pinpad plugin.
   2. Otherwise, if an old pinpad plugin is found for that reader, the middleware will send pin
      related commands via the old pinpad plugin, using the old API.
   3. Otherwise, if the reader responds correctly to a CCID_IOCTL_GET_FEATURE_REQUEST,
      the pin related commands are directly sent to that reader.
   4. Otherwise, the middleware assumes the reader is no pinpad reader and will display a
      PIN dialog if a PIN is needed by the card.

Implementing a pinpad plugin

A pinpad plugin is a shared library (a dll file on windows, an so file on Linux).
This plugin has to be placed on a specific location in order for the middleware to find it:
    - On Linux and Mac OS X: $PREFIX/lib/beidpp ($PREFIX is usually /usr/local)
    - On Windows: <systemdir>\beidpp (<systemdir> is the Windows system directory,
        usually C:\WINDOWS\system32 on XP and Vista)
Additionally, the plugin name should start with “beidpp2” on Windows or “libbeidpp2” on Linux,
at least for the current version of the pinpad plugin API (V2).

The pinpad plugins should implement an API in the C programming language that is specified
in the pinpad2.h header file below. There are 2 functions that should be implemented:
1. The EIDMW_PP2_Init() function is called first by the middleware to detect if the plugin
    supports this reader (and card). If so, this function should return SCARD_S_SUCCESS and
    optionally messages can be specified that should be displayed by the middleware before
    asking the pinpad plugin/reader to perform a PIN operation. If no message strings are
    returned the pinpad reader will use default messages; it is also possible to specify that the
    middleware shouldn’t display any message (e.g. if the pinpad plugin will do it itself).
2. The EIDMW_PP2_Command() function is called each time an ScardControl() command
    should be sent to the pinpad reader in the case it would be a CCID reader. In fact, the
    main parameters of this function are the control code and CCID data that would be sent to
    a CCID reader. Other useful parameters are the type of PIN and the PIN operation that
    should be done, this way the pinpad reader could show more relevant messages on the
    pinpad reader display (if present).

If a pinpad plugin is implemented for a CCID pinpad reader, the plugin could directly send the
data in the EIDMW_PP2_Command() to the reader via an SCardControl() function (and
perhaps do additional things such as telling the reader what to display on the reader’s display
via a propietory command). Non-CCID pinpad readers should parse the data in the
EIDMW_PP2_Command() and use it to create proprietory commands for the pinpad reader.

For more info about these functions, see the pinpad2.h header file below.

Example

Below is an example of the SCardControl() functions that are called by the middleware. In case
of a pinpad plugin, they are the data that is sent via the EIDMW_PP2_Command() function.

The first SCardControl() command is always a request for the feature list:
        Control code sent: CCID_IOCTL_GET_FEATURE_REQUEST
        Data sent: empty
In case of a pinpad reader (or plugin), the following data could be returned (for example):
        06 04 00 31 20 30 07 04 00 31 20 34
        This means that the pinpad reader in question supports the CCID_VERIFY_DIRECT
        command (tag 06) with control code 0x00312030, and the CCID_CHANGE_DIRECT
        command (tag 07) with control code 0x00312034).

If a PIN has to be verified by this reader, an SCardControl() is sent as follows:
        Control code sent: 0x00312030
        Control data sent: 1E 1E 89 47 04 08 04 02 01 CD CD 00 00 00 00 0D 00 00 00 00 20
        00 01 08 20 FF FF FF FF FF FF FF (these are the contents of the
        EIDMW_PP_VERIFY_CCID struct in the pinpad2.h header file below)
The returned data should be 2 bytes, containing either the SW1-SW2 from the card or
otherwise a CCID status word from the reader (64 00 for a timeout, 64 01 for a Cancel, ...)
If a PIN change is requested, the middleware sends an SCardControl() function with:
        Control code sent: 0x00312034
        Control data sent: 1E 1E 01 47 04 00 08 0C 04 03 02 03 CD CD 00 00 00 00 00 00 15
        00 00 00 00 24 00 01 10 20 FF FF FF FF FF FF FF 20 FF FF FF FF FF FF FF (these are
        the contents of the EIDMW_PP_CHANGE_CCID struct in pinpad2.h)
The returned data is also 2 bytes and has the same meaning as for a PIN verification.

The meaning of the control data that is sent can be found in the CCID standard [1] or briefly in
the pinpad2.h header file below.

API to be implemented by pinpad plugins (pinpad2.h)
/*
 * This API should be implemented by libraries (also called
 * plugins) that want to support a pinpad reader for use
 * with the eID middleware.
 *
 * The middleware has support for CCID readers; see the PCSC V2
 * part 10 standard and the USB CCID specifications.
 *
 * However these standards don't specify how to display messages
 * on the reader's display. Because the middleware will show
 * some explanation on the PC whenever a pinpad operation needs
 * to be done, this restriction is not important for pinpad
 * readers without display.
 *
 * But for readers with display, it is important that the correct
 * message be displayed, as to avoid confusion between the different
 * PINs on the eID card and the fact that the "PIN change command"
 * is used for both PIN change and PIN unblock.
 *
 * In this case, as well as for non-CCID pinpad readers or readers
 * that offer extra functionality, a library (a DLL on Windows, an
 * .so file on Linux and a .dylib file on Mac) can be made that
 * exports the 2 functions below.
 * The middleware will first try to load this library and check if
 * it can be used by mean of the EIDMW_PP_Init() function. If so, then
 * all pinpad functionality will be requested through this library.
 *
 * To allow the middleware to find the library, it must be placed in
 * the following directory:
 * - On Linux and Mac OS X: /usr/local/lib/beidpp
 * - On Windows: <systemdir>\beidpp
 * (<systemdir> is the Windows system folder, usually this is
 * C:\WINDOWS\system32 on WinXP and Vista)
 * And the name of the library should start with "beidpp2" on
 * Windows, and with "libbeidpp2" on Linux and Mac.
 *
 * Future, incompatible versions of this library will start with "beidpp3" etc.
 *
 * Remark: for the middleware for other country, "beid" should be
 * replace by e.g. "pteid", "ileid", ...
 */

#ifndef __PINPAD2_H__
#define __PINPAD2_H__

#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32) || defined(WIN32)
#ifdef EIDMW_PP_IMPORT
#define EIDMW_PP_API __declspec(dllimport)
#else
#define EIDMW_PP_API __declspec(dllexport)
#endif
#else
#define EIDMW_PP_API
#endif

#include <winscard.h>

#ifndef WIN32
#include "wintypes.h"
#endif

#define PTEID_MINOR_VERSION             0

#define EIDMW_PP_TYPE_AUTH   0x00 /* The Authentication PIN/PUK */
#define EIDMW_PP_TYPE_SIGN   0x01 /* The Signature PIN/PUK */
#define EIDMW_PP_TYPE_ADDR    0x02 /* The Address PIN/PUK */
#define EIDMW_PP_TYPE_ACTIV  0x03 /* The Activation PIN */
#define EIDMW_PP_TYPE_UNKNOWN 0xFF /* Unkonwn PIN/PUK */

#define EIDMW_PP_OP_VERIFY       0x01 /* PIN verification */
#define EIDMW_PP_OP_CHANGE         0x02 /* PIN change */
#define EIDMW_PP_OP_UNBLOCK_NO_CHANGE      0x03 /* PIN unblock without PIN change */
#define EIDMW_PP_OP_UNBLOCK_CHANGE      0x04 /* PIn unblock plus PIN change */
#define EIDMW_PP_OP_UNBLOCK_MERGE_NO_CHANGE 0x05 /* PIN unblock using PUK merge, without PIN change*/
#define EIDMW_PP_OP_UNBLOCK_MERGE_CHANGE 0x06 /* PIN unblock using PUK merge, plus PIN change */

#define DLG_INFO_CHARS           2000

#define PP_LANG_EN      0x0409
#define PP_LANG_NL      0x0813
#define PP_LANG_FR      0x080c
#define PP_LANG_DE      0x0407

/**
 * This allows libraries to display their own messages in the dialogs that
 * appear before each pinpad operation, in order to instruct users what to do.
 * If a message is left to "" then the default message will be used.
 * If a message is set to "r" then the middleware won't show a dialog,
 * this allows libraries to implement their own dialogs.
 * The messages should contain UTF-8 encoded plain text or html code and can be
 * at most DLG_INFO_CHARS bytes in length; this memory has already been allocated.
 * LIMITATION: double quote signs " or \" are not allowed inside the strings!
 */
typedef struct {
            char *csVerifyInfo;
            char *csChangeInfo;
            char *csUnblockNoChangeInfo;
            char *csUnblockChangeInfo;
            char *csUnblockMergeNoChangeInfo;
            char *csUnblockMergeChangeInfo;
            char *csRfu1;                /* NULL */
            char *csRfu2;                /* NULL */
            char *csRfu3;                /* NULL */
            char *csRfu4;                /* NULL */
} tGuiInfo;

typedef long (*EIDMW_PP2_INIT)(unsigned char, SCARDCONTEXT, SCARDHANDLE,
          const char *, unsigned long, tGuiInfo *, unsigned long, void *);
typedef long (*EIDMW_PP2_COMMAND)(SCARDHANDLE, int, const unsigned char *,
          DWORD, unsigned char *, DWORD, DWORD *, unsigned char,
          unsigned char, unsigned long, void *);

/**
 * This function is called after loading the pinpad library, it is used by the middleware
 * to see if the pinpad library supports this reader; and for the pinpad library itself
 * to make any intialisations.
 *
 * - ucMinorVersion: (IN) indicates a change in the API that is compatible with other
 * ucMinorVersions of this library, current value = PTEID_MINOR_VERSION.
 * - hCtx: (IN) the SCARDCONTEXT handle that was obtained by a call to SCardEstablishContext()
 * - hCard: (IN) the SCARDHANDLE that was obtained by a call to SCardConnect()
 * - csReader: (IN) the PCSC reader name as obtained by a call to SCardListReaders()
 * - ulLanguage: (IN) USB LANGID code (http://www.usb.org/developers/docs) for the language
 * of the messages to be displayed on the pinpad reader
 * Examples are 0x0409 for English (UK), 0x0813 for Dutch (BE),
 *      0x080c for French (BE), 0x0407 for German.
 * - pGuiInfo: (OUT) strings to be displayed on PC before each pinpad operation,
 * leave them unchanged (NULL) to use the default strings. The size that has
 * been allocated for each string is 2000 (DLG_INFO_CHARS) bytes.
 * Set to "r" if nothing should be displayed on the PC (the pinpad lib will
 * take care of this).
 * - ulRfu: reserved for future use, set to 0 for this ucMinorVersion
 * - pRfu: reserved for future use, set to NULL for this ucMinorVersion
 *
 * Returns SCARD_ERROR_SUCCESS upon success, or another (preferably PCSC) error if this pinpad
 * library doesn't support this reader or something else went wrong.
 * In the case SCARD_ERROR_SUCCESS is returned, this pinpad library will be used
 * by the middleware for pinpad operation. Otherwise, the middleware will continue
 * searching for other pinpad libraries that may support this reader.
 */
EIDMW_PP_API long EIDMW_PP2_Init(
            unsigned char ucMinorVersion,
            SCARDCONTEXT hCtx, SCARDHANDLE hCard, const char *csReader,
            unsigned long ulLanguage,
            tGuiInfo *pGuiInfo,
            unsigned long ulRfu, void *pRfu);

/**
 * This function is called for a "Get Feature Request" with control = CM_IOCTL_GET_FEATURE_REQUEST
 * (right after a successfull call to EIDMW_PP_Init())
 * and when a pinpad operation (verify, change, unblock) is needed.
 *
 * The following ioctl codes are recognized and used by the middleware
 * - For PIN verification:
 *     FEATURE_VERIFY_PIN_DIRECT
 *     FEATURE_VERIFY_PIN_START and FEATURE_VERIFY_PIN_FINISH
 * - For PIN change and unblock:
 *     FEATURE_MODIFY_PIN_DIRECT
 *     FEATURE_MODIFY_PIN_START and FEATURE_MODIFY_PIN_FINISH
 *
 * - The first 7 parameters are identical to the ones given in an SCardControl()
 * command, as specified in part 10 of the PCSC standard.
 * In case of a standard CCID pinpad reader without display, this function could
 * directly 'forward' these parameters to an SCardControl() function.
 * - ucPintype: one of EIDMW_PP_TYPE_AUTH, ..., EIDMW_PP_TYPE_ACTIV; is ignored for
 * a "Get Feature Request"
 * - ucOperation: one of EIDMW_PP_OP_VERIFY, ..., EIDMW_PP_OP_UNBLOCK_MERGE; is
 * ignored for a "Get Feature Request"
 * - ulRfu: reserved for future use, set to 0 for this ucMinorVersion
 * - pRfu: reserved for future use, set to NULL for this ucMinorVersion
 *
* Returns SC_ERROR_SUCCESS upon success, or another (preferably) PCSC error code otherwise.
*/
EIDMW_PP_API long EIDMW_PP2_Command(
         SCARDHANDLE hCard, int ioctl,
         const unsigned char *pucSendbuf, DWORD dwSendlen,
         unsigned char *pucRecvbuf, DWORD dwRrecvlen, DWORD *pdwRrecvlen,
         unsigned char ucPintype, unsigned char ucOperation,
         unsigned long ulRfu, void *pRfu);


/////////////////////////////////// CCID things ///////////////////////////////
// More info:
// http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.01.6.pdf
// http://www.usb.org/developers/devclass_docs/DWG_Smart-Card_CCID_Rev110.pdf
///////////////////////////////////////////////////////////////////////////////

#ifndef SCARD_CTL_CODE
#define SCARD_CTL_CODE(code) (0x42000000 + (code))
#endif
#define CCID_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400)

#define CCID_VERIFY_START 0x01
#define CCID_VERIFY_FINISH 0x02
#define CCID_VERIFY_DIRECT 0x06
#define CCID_CHANGE_START 0x03
#define CCID_CHANGE_FINISH 0x04
#define CCID_CHANGE_DIRECT 0x07

/* Big endian encoding to 2 bytes */
inline void ToUchar2(unsigned long ulIn, unsigned char *pucOut2)
{
           pucOut2[0] = (unsigned char) (ulIn % 256);
           pucOut2[1] = (unsigned char) (ulIn / 256);
}

/* Big endian encoding to 4 bytes */
inline void ToUchar4(unsigned long ulIn, unsigned char *pucOut4)
{
           pucOut4[0] = (unsigned char) (ulIn % 256);
           ulIn /= 256;
           pucOut4[1] = (unsigned char) (ulIn % 256);
           ulIn /= 256;
           pucOut4[2] = (unsigned char) (ulIn % 256);
           pucOut4[3] = (unsigned char) (ulIn / 256);
}

// The structs below need packing with 1-byte alignment
#pragma pack(push, pinpad2, 1)

#define PP_APDU_MAX_LEN 40

/**
 * Data to be sent during a PIN verification
 * (or unblock without PIN change).
 */
typedef struct
{
           unsigned char bTimerOut; // timeout in seconds (0: default timeout)
           unsigned char bTimerOut2; // timeout in seconds after first key stroke
           unsigned char bmFormatString; // formatting options
           unsigned char bmPINBlockString; // length (in bytes) of the PIN block
           unsigned char bmPINLengthFormat; // where (if needed) to put the PIN length
           unsigned char wPINMaxExtraDigit[2]; // min and max number of PIN digits
        unsigned char bEntryValidationCondition; // e.g. 0x02: "OK" button pressed
        unsigned char bNumberMessage; // number of messages to display
        unsigned char wLangId[2]; // LANG_ID code
        unsigned char bMsgIndex; // Message index (should be 00)
        unsigned char bTeoPrologue[3]; // T=1 block prologue field to use (fill with 00)
        unsigned char ulDataLength[4]; // length of the following field
        unsigned char abData[PP_APDU_MAX_LEN]; // APDU to send to the card (to be completed by the reader)
} EIDMW_PP_VERIFY_CCID;

/**
 * Data to be sent during a PIN change
 * (or unblock with PIN change).
 */
typedef struct
{
           unsigned char bTimerOut; // timeout is seconds (00 means use default timeout)
           unsigned char bTimerOut2; // timeout in seconds after first key stroke
           unsigned char bmFormatString; // formatting options
           unsigned char bmPINBlockString; // length (in bytes) of the PIN block
           unsigned char bmPINLengthFormat; // where (if needed) to put the PIN length
           unsigned char bInsertionOffsetOld; //insertion position offset in bytes for the current PIN
           unsigned char bInsertionOffsetNew; // insertion position offset in bytes for the new PIN
           unsigned char wPINMaxExtraDigit[2]; // min and max number of PIN digits
           unsigned char bConfirmPIN; // flags governing need for confirmation of new PIN
           unsigned char bEntryValidationCondition; // e.g. 0x02: "OK" button pressed
           unsigned char bNumberMessage; // number of messages to display
           unsigned char wLangId[2]; // LANG_ID code to specify the language
           unsigned char bMsgIndex1; // index of 1st prompting message
           unsigned char bMsgIndex2; // index of 2d prompting message
           unsigned char bMsgIndex3; // index of 3d prompting message
           unsigned char bTeoPrologue[3]; // T=1 block prologue field to use (fill with 00)
           unsigned char ulDataLength[4]; // length of the following field
           unsigned char abData[PP_APDU_MAX_LEN]; // APDU to send to the card (to be completed by the reader)
} EIDMW_PP_CHANGE_CCID;

#pragma pack(pop, pinpad2)

#ifdef __cplusplus
}
#endif

#endif



References

[1] CCID related standards:
 http://www.pcscworkgroup.com/specifications/files/pcsc10_v2.01.6.pdf
 http://www.usb.org/developers/devclass_docs/DWG_Smart-Card_CCID_Rev110.pdf

[2] USB LANGID code: http://www.usb.org/developers/docs

								
To top