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
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.