How to know the PIN sizes supported by a pinpad reader?

The problem


Some pinpad readers (like the Gemalto PC pinpad v1) does not support PIN of less than 4 digits or more than 8 digits. The problem is that the Gemalto PC pinpad v1 reader is very strict and instead of ignoring unsupported options the reader reject the CCID command.

For example:
00000026 ifdhandler.c:1291:IFDHControl() ControlCode: 0x42330006, usb:08e6/3478:libusb:002:034 (lun: 0)
00000021 Control TxBuffer: 1E 1E 02 10 00 10 04 02 00 00 00 00 00 00 00 15 00 00 00 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00000009 commands.c:335:SecurePINVerify() Correct bNumberMessage for GemPC Pinpad (was 0)
00000009 openct/proto-t1.c:571:t1_build() more bit: 0
00000022 -> 000000 69 24 00 00 00 00 41 00 00 00 00 1E 02 10 00 10 04 02 01 00 00 00 00 40 15 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00006255 <- 000000 80 00 00 00 00 00 41 40 0F 00 
00000028 commands.c:1319:CCID_Receive error on byte 15
00000011 Control RxBuffer: 
00000012 ifdwrapper.c:644:IFDControl() Card not transacted: 612
00000011 ifdwrapper.c:646:IFDControl() ControlCode: 0x42330006 BytesReturned: 0
00000031 TxBuffer 1E 1E 02 10 00 10 04 02 01 00 00 00 00 40 15 15 00 00 00 00 20 00 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00000010 RxBuffer 
00000009 winscard_svc.c:692:ContextThread() CONTROL rv=0x80100016 for client 5

Here the application (OpenSC in this case) uses PC/SC v2 Part 10 to request a secure verify PIN (using SCardControl() with the ControlCode: 0x42330006.

Then the CCID driver creates a CCID Secure frame (PC_to_RDR_Secure = 0x69) and sends it to the reader. The reader returns an error on byte 15 which has the value 0x10 (or 16 in decimal) and is the first byte of the wPINMaxExtraDigit field (the second byte is 0x04). The reader does not support the value 16 for Maximum PIN size in digit.

The driver returns the generic error 612 (IFD_COMMUNICATION_ERROR) and PC/SC returns the error 0x80100016 (SCARD_E_NOT_TRANSACTED) to the application. The application has no way to know why the command failed. Someone has to look at the very low level logs to discover that the probelm is with wPINMaxExtraDigit.

The PC/SC solution: FEATURE_GET_TLV_PROPERTIES


Since PCSCv2 part 10 v2.02.07 from March 2010 a new feature is available: FEATURE_GET_TLV_PROPERTIES. It is very important to have a solution portable and documented in an "official" document. This feature is used to return a TLV formatted list of properties of the reader (or driver). Two properties are bMinPINSize and bMaxPINSize.

The only driver I know that supports FEATURE_GET_TLV_PROPERTIES is my CCID driver in version 1.3.12 release 8 May 2010. Maybe other drivers will support FEATURE_GET_TLV_PROPERTIES some time in the future.

Do not expect any thing from Microsoft. Their CCID driver and PCSC middleware are frozen in ice since some time (in years).

The sample codes

C code

If you use the sample code scardcontrol.c (772 lines of C) provided with my CCID driver you can see some results. For example with a Gemalto PC Pinpad v1 connected the output is:

$ ./scardcontrol 
SCardControl sample code
V 1.4 © 2004-2010, Ludovic Rousseau 

THIS PROGRAM IS NOT DESIGNED AS A TESTING TOOL!
Do NOT use it unless you really know what you do.

SCardListReaders: OK

Available readers (use command line argument to select)
0: Gemalto GemPC Pinpad 00 00

Using reader: Gemalto GemPC Pinpad 00 00
 Protocol: 0
SCardConnect: OK

SCardControl: OK

 TLV (24): 06 04 42 33 00 06 07 04 42 33 00 07 0A 04 42 33 00 0A 12 04 42 33 00 12 
SCardControl(CM_IOCTL_GET_FEATURE_REQUEST): OK

Reader supports: FEATURE_VERIFY_PIN_DIRECT
Reader supports: FEATURE_MODIFY_PIN_DIRECT
Reader supports: FEATURE_IFD_PIN_PROPERTIES
Reader supports: FEATURE_GET_TLV_PROPERTIES

SCardControl(GET_TLV_PROPERTIES): OK

GET_TLV_PROPERTIES (37): 01 02 00 00 03 01 00 08 13 47 65 6D 54 77 52 43 32 2D 56 32 2E 30 30 2D 47 4C 30 33 06 01 04 07 01 08 02 01 02 

Display all the properties:
 wLcdLayout: 0x0000
 bTimeOut2: 0x00
 sFirmwareID: GemTwRC2-V2.00-GL03
 bMinPINSize: 0x04
 bMaxPINSize: 0x08
 bEntryValidationCondition: 0x02

Find a specific property:
 bEntryValidationCondition: 2
 bMaxPINSize: 8

 Reader: Gemalto GemPC Pinpad 00 00 (length 27 bytes)
 State: 0x20034
 Prot: 0
 ATR (length 20 bytes): 3B FA 94 00 00 81 31 20 43 80 65 A2 01 01 01 3D 72 D6 43 21
SCardStatus: OK

 Protocol: 2
SCardReconnect: OK

Select applet:  00 A4 04 00 06 A0 00 00 00 18 FF
 card response: 90 00
SCardTransmit: OK

 Secure modify PIN
 command: 00 00 82 04 00 00 04 08 04 03 02 03 04 09 00 00 00 00 00 00 0D 00 00 00 00 24 00 00 08 30 30 30 30 30 30 30 30
Enter your PIN:  card response: 90 00
SCardControl: OK


modify PIN dump:  00 40 00 00 FF
 card response: 00 24 00 00 08 31 32 33 34 35 36 37 38 90 00
SCardTransmit: OK

SCardDisconnect: OK

This sample code is not specifically designed to just get the min and max PIN sizes. So many lines are of no value for us here.

The useful lines here are:
bMinPINSize: 0x04
 bMaxPINSize: 0x08

Python code

A much simpler source code uses the Python wrapper (described here).

Source code (13 lines)

#!/usr/bin/env python

from smartcard.pcsc.PCSCPart10 import getTlvProperties
from smartcard.pcsc.PCSCReader import readers, SCARD_SHARE_DIRECT

# get a connection to the first reader
cc = readers()[0].createConnection()
cc.connect(mode=SCARD_SHARE_DIRECT)

print "\nTlvProperties:"
properties = getTlvProperties(cc)
for k in properties.keys():
    print " %s: %s" % (k, properties[k])

Output

TlvProperties:
 PCSCv2_PART10_PROPERTY_sFirmwareID: GemTwRC2-V2.00-GL03
 PCSCv2_PART10_PROPERTY_bTimeOut2: 0
 PCSCv2_PART10_PROPERTY_bEntryValidationCondition: 2
 raw: [1, 2, 0, 0, 3, 1, 0, 8, 19, 71, 101, 109, 84, 119, 82, 67, 50, 45, 86, 50, 46, 48, 48, 45, 71, 76, 48, 51, 6, 1, 4, 7, 1, 8, 2, 1, 2]
 PCSCv2_PART10_PROPERTY_wLcdLayout: 0
 PCSCv2_PART10_PROPERTY_bMaxPINSize: 8
 PCSCv2_PART10_PROPERTY_bMinPINSize: 4

getTlvProperties() returns a dictionnay. The entry for the "raw" key is just the complete buffer without interpretation.

Another related problem: bEntryValidationCondition

Another limitation of the Gemalto PC pinpad v1 is bEntryValidationCondition. This field is used by the PC_to_RDR_Secure command (verify and modify PIN) to tell what conditions shall be used by the reader to validate an entry. According to the CCID specification it can be:
The value is a bit wise OR operation.
  • 01h Max size reached
  • 02h Validation key pressed
  • 04h Timeout occurred
But the Gemalto PC pinpad v1 only supports the 02h value (Validation key pressed). Any other value is rejected by the reader and the CCID command fails. The problem is that the PCSC application has no way to know what is supported by the reader.

Solution 1

So my CCID driver has a special treatment for this reader and bEntryValidationCondition is reset to 02h even if it is not what the applications requested.

00000032 ifdhandler.c:1291:IFDHControl() ControlCode: 0x42330007, usb:08e6/3478:libusb:002:039 (lun: 0)
00000031 Control TxBuffer: 00 00 82 04 00 00 04 08 04 03 07 03 04 09 00 00 00 00 00 00 0D 00 00 00 00 24 00 00 08 30 30 30 30 30 30 30 30 
00000014 commands.c:583:SecurePINModify() Correct bEntryValidationCondition for GemPC Pinpad (was 7)
00000012 openct/proto-t1.c:571:t1_build() more bit: 0
00000228 commands.c:679:SecurePINModify() readTimeout: 30000
00000036 -> 000000 69 21 00 00 00 00 51 00 00 00 01 00 82 04 00 00 04 08 04 03 02 03 04 09 00 00 00 00 40 0D 00 24 00 00 08 30 30 30 30 30 30 30 30

Solution 2


FEATURE_GET_TLV_PROPERTIES can also return a bEntryValidationCondition indicating what bits are supported by the reader.

Conclusion


Many variable features of a reader or driver can't be known by a PC/SC application. Or the application need to be configured specifically for one reader (or a set of readers).

It is now possible, using the FEATURE_GET_TLV_PROPERTIES mechanism to retrieve some of reader specificity and adapt the application.