Friday, December 19, 2014

Great OpenPGP smart card article (in French)

gouttegd published a very good article about the configuration and use of an OpenPGP smart card. The article is "“OpenPGP card“, une application cryptographique pour carte à puce" and is written in French (sorry).

Sommaire

OS X Yosemite bug: SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER

This is part if the series: "OS X Yosemite and smart cards: known bugs".

SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER

SCardStatus() does not correctly report the reader name length. The terminating NUL character of the reader name is not counted. So a second call to SCardStatus() using the size returned by the first SCardStatus() call will fail with 0x80100008 (that is SCARD_E_INSUFFICIENT_BUFFER).

It is a really stupid bug and very easy to fix for Apple.

See also

"Problem with SCardStatus on MAC OS X 10.10 (Yosemite)" at https://smartcardservices.macosforge.org/trac/ticket/140.

Apple bug report #19306215 "PC/SC SCardStatus returns SCARD_E_INSUFFICIENT_BUFFER".

Sample code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
 SCARDCONTEXT hContext;
 LPSTR mszReaders;
 DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardEstablishedContext: 0x%08x\n",err);
  return -1;
 }

 DWORD cchReaders = 0;
 err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
 if (err != 0) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 mszReaders = calloc(cchReaders, sizeof(char));
 if (!mszReaders) {
  printf("calloc\n");
  return -1;
 }
 err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 printf("Reader: %s\n", mszReaders);

 SCARDHANDLE hCard;
 DWORD dwActiveProtocol;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }

 /* 1st SCardStatus call to get the reader name length */
 char name[100];
 DWORD len;
 err = SCardStatus(hCard, NULL, &len, NULL, NULL, NULL, NULL);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 printf("reader name length: %d\n", len);
 //len += 1;

 /* 2nd SCardStatus call to get the reader name value */
 err = SCardStatus(hCard, name, &len, NULL, NULL, NULL, NULL);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 printf("Reader name: %s (%ld)\n", name, strlen(name));

 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 SCardReleaseContext(hContext);

 return 0;
}

Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main

$ ./main
Reader: Gemalto PC Twin Reader
reader name length: 22
SCardStatus: 0x80100008

If I uncomment the line: len += 1; I get:
$ ./main
Reader: Gemalto PC Twin Reader
reader name length: 22
Reader name: Gemalto PC Twin Reader (22)

Note that the reader length returned by the first SCardStatus() call is identical to the value returned by strlen(name). And strlen() does NOT includes the terminating NUL character.

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main

$ ./main
Reader: Gemalto PC Twin Reader 00 00
reader length: 29
Reader name: Gemalto PC Twin Reader 00 00 (28)

Note that the length returned by the first SCardStatus() call is strlen(name) + 1.

Known workaround

Add 1 to the reader length returned by SCardStatus().

Tuesday, December 16, 2014

OS X Yosemite bug: SCardStatus() after a card reset

This is part if the series: "OS X Yosemite and smart cards: known bugs".

SCardStatus() after a card reset

SCardStatus() does not corectly detect card reset any more on OS X 10.10 Yosemite.

If a card has been reseted using another PC/SC context then SCardStatus() will not fail with 0x80100068 (for SCARD_W_RESET_CARD) as before but will succeed instead.
The problem will be detected later by, for example, the next SCardTransmit() returning SCARD_W_RESET_CARD.

The problem is similar to the one described in OS X Yosemite bug: SCardBeginTransaction() after a card reset.

What is strange is that SCardTransmit() erroneously returns SCARD_W_RESET_CARD after a SCardReconnect(..., SCARD_RESET_CARD, ...) (see OS X Yosemite bug: SCardReconnect) but SCardStatus() do not return SCARD_W_RESET_CARD in the same case. The card reset (or not) state is managed in a "funny" way inside PC/SC on Yosemite.

See also

Apple bug report #19264087 "PC/SC function SCardStatus() fails to fail after a card reset"
#19264087 closed as a duplicate of #18689292.

Sample code

#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
 SCARDCONTEXT hContext;
 LPSTR mszReaders;
 DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardEstablishedContext: 0x%08x\n",err);
  return -1;
 }

 DWORD cchReaders = 0;
 err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
 if (err != 0) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 mszReaders = calloc(cchReaders, sizeof(char));
 if (!mszReaders) {
  printf("calloc\n");
  return -1;
 }
 err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 printf("Reader: %s\n", mszReaders);

 SCARDHANDLE hCard;
 DWORD dwActiveProtocol;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }

 /* create a second PC/SC handle and reset the card */
 SCARDHANDLE hCard2;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard2, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }
 err = SCardDisconnect(hCard2, SCARD_RESET_CARD);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardDisconnect: 0x%08x\n",err);
 }

 /* SCardStatus should fail with SCARD_W_RESET_CARD */
 char name[100];
 DWORD len = sizeof name;
 DWORD dwState, dwProtocol;
 unsigned char atr[MAX_ATR_SIZE];
 DWORD atr_len = sizeof atr;
 err = SCardStatus(hCard, name, &len, &dwState, &dwProtocol, atr, &atr_len);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardStatus: 0x%08x\n",err);
  return -1;
 }
 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 SCardReleaseContext(hContext);

 return 0;
}

Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main

An error 0x80100068 (for SCARD_W_RESET_CARD) is expected here.
$ ./main 
Reader: Gemalto PC Twin Reader

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main

$ ./main
Reader: Gemalto PC Twin Reader 00 00
SCardStatus: 0x80100068

Known workaround

None known.

Modify your code to check for  SCARD_W_RESET_CARD returned by SCardTransmit() even after a successful SCardStatus().

OS X Yosemite bug: SCardBeginTransaction() after a card reset

This is part if the series: "OS X Yosemite and smart cards: known bugs".

SCardBeginTransaction() after a card reset

SCardBeginTransaction() does not corectly detect card reset any more on OS X 10.10 Yosemite.

If a card has been reseted using another PC/SC context then SCardBeginTransaction() will not fail with 0x80100068 (for SCARD_W_RESET_CARD) as before but will succeed instead.
The problem will be detected later by, for example, the next SCardTransmit() returning SCARD_W_RESET_CARD.

The goal of a PC/SC transaction was to guarantee that you have an exclusive and successful access to the card. This mechanism is broken on Yosemite.

See also

Apple bug report #19263926 "PC/SC SCardBeginTransaction() fails after a card reset"
#19263926 closed as a duplicate of #18689292.

Sample code

#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
 SCARDCONTEXT hContext;
 LPSTR mszReaders;
 DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardEstablishedContext: 0x%08x\n",err);
  return -1;
 }

 DWORD cchReaders = 0;
 err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
 if (err != 0) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 mszReaders = calloc(cchReaders, sizeof(char));
 if (!mszReaders) {
  printf("calloc\n");
  return -1;
 }
 err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardListReaders: 0x%08x\n",err);
  return -1;
 }
 printf("Reader: %s\n", mszReaders);

 SCARDHANDLE hCard;
 DWORD dwActiveProtocol;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }

 /* create a second PC/SC handle and reset the card */
 SCARDHANDLE hCard2;
 err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard2, &dwActiveProtocol);
 if (err != SCARD_S_SUCCESS) {
  printf("ScardConnect: 0x%08x\n",err);
  return -1;
 }
 err = SCardDisconnect(hCard2, SCARD_RESET_CARD);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardReconnect: 0x%08x\n",err);
 }

 /* SCardBeginTransaction should fail with SCARD_W_RESET_CARD */
 err = SCardBeginTransaction(hCard);
 if (err != SCARD_S_SUCCESS) {
  printf("SCardBeginTransaction: 0x%08x\n",err);
  return -1;
 }
 SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 SCardReleaseContext(hContext);

 return 0;
}

Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main

An error 0x80100068 (for SCARD_W_RESET_CARD) is expected here.
$ ./main 
Reader: Gemalto PC Twin Reader

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main

$ ./main 
Reader: Gemalto PC Twin Reader 00 00
SCardBeginTransaction: 0x80100068

Known workaround

None known.

Modify your code to check for  SCARD_W_RESET_CARD returned by SCardTransmit() even inside a PC/SC transaction.

OS X Yosemite bug: SCardReconnect

This is part if the series: "OS X Yosemite and smart cards: known bugs".

SCardReconnect

SCardReconnect() has a bad side effect on Yosemite.
After a SCardReconnect(..., SCARD_RESET_CARD, ...) the next SCardTransmit() will fail with the error code 0x80100068 that is SCARD_W_RESET_CARD.

This should not happen since SCardReconnect() should reconnect (sic) to the card after the card has been reseted.

See also

"[OSX 10.10] After connect and SCardReconnect, SCardTransmit raises card reset" https://smartcardservices.macosforge.org/trac/ticket/136

Apple bug report #19262854 "PC/SC function SCardReconnect does not work as expected"

Sample code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(int argc, const char * argv[]) {
    SCARDCONTEXT hContext;
    LPSTR mszReaders;
    DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    if (err != SCARD_S_SUCCESS) {
        printf("ScardEstablishedContext: 0x%08x\n",err);
        return -1;
    }

    DWORD cchReaders = 0;
    err = SCardListReaders(hContext, "SCard$AllReaders", NULL, &cchReaders);
    if (err != 0) {
        printf("ScardListReaders: 0x%08x\n",err);
        return -1;
    }
    mszReaders = calloc(cchReaders, sizeof(char));
    if (!mszReaders) {
        printf("calloc\n");
        return -1;
    }
    err = SCardListReaders(hContext,"SCard$AllReaders", mszReaders, &cchReaders);
    if (err != SCARD_S_SUCCESS) {
        printf("ScardListReaders: 0x%08x\n",err);
        return -1;
    }
    printf("Reader: %s\n", mszReaders);

    SCARDHANDLE hCard;
    DWORD dwActiveProtocol;
    err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        printf("ScardConnect: 0x%08x\n",err);
        return -1;
    }

    unsigned char cmd[] = {0, 0, 0, 0};
    unsigned char resp[255];
    DWORD resp_len = sizeof resp;
    SCARD_IO_REQUEST * pci;

    err = SCardReconnect(hCard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_RESET_CARD, &dwActiveProtocol);
    if (err != SCARD_S_SUCCESS) {
        printf("SCardReconnect: 0x%08x\n",err);
    }

    if (SCARD_PROTOCOL_T0 == dwActiveProtocol)
        pci = SCARD_PCI_T0;
    else
        pci = SCARD_PCI_T1;
    err = SCardTransmit(hCard, pci, cmd, sizeof cmd, pci, resp, &resp_len);
    if (err != SCARD_S_SUCCESS) {
        printf("SCardTransmit: 0x%08x\n",err);
    }
    SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    SCardReleaseContext(hContext);

    return 0;
}


Result (on Yosemite)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main

$ ./main 
Reader: Gemalto PC Twin Reader
SCardTransmit: 0x80100068

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main
main.c: In function ‘main’:
main.c:57:7: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]
main.c:59:7: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default]

No error expected:
$ ./main 
Reader: Gemalto PC Twin Reader 00 00

Known workaround

You can add an second call to SCardReconnect(..., SCARD_LEAVE_CARD, ...) after the first one to force a card reconnection.

Or you can replace the bogus SCardReconnect() by the sequence:
SCardDisconnect(..., SCARD_RESET_CARD);
SCardConnect(...);

I could not find an elegant way to write a macro to automate the code change. The problem is that SCardConnect() needs parameters (PC/SC context and reader name) that are not available from the SCardReconnect() call.

Monday, December 15, 2014

Feitian bR301 driver for iOS now Free Software

Bluetooth reader

Feitian sells the bR301 smart card reader. This reader uses Blutooth to communicate with a host. The idea is to be able to "connect" the reader to a smart phone (iOS and Android).

The reader can also be connected using a USB cable. In that case the reader is a normal CCID reader and is already listed by my CCID driver in the "Should work but untested by me" list.


Software Development Kit (SDK)

The SDK (containing the reader drivers) is not directly available from Feitian web site. But you can get it from a reseller web site at http://cartesapuce-discount.com/en/smart-card-reader-usb/74-lecteur-de-cartes-a-puce-usb-cardman-3121.html. The SDK URL is http://download.ftsafe.com/files/reade/bR301_UB_V2.19_20141008.zip.

After I had a look inside the SDK I discovered that the iOS driver bR301_UB_V2.19_20141008/SDK/iOS/Library/NON-PCSC API/lib/libiRockey301_ccid_debug_NPCSC_V1.29.0.a is in fact a derivated work from my CCID driver.

It is perfectly fine to modify my CCID driver and provide a derivated work. The only condition is to respect the GNU LGPL v2.1+ license used by my CCID driver. This was not the case with the Feitian driver.

Free Software version

After some email exchanges with Feitian to explain the situation they decided to release their iOS driver under the same license as my CCID driver: the GNU LGPL v2.1+ license. The Feitian iOS driver is hosted at github: https://github.com/entersafe/bR301_iOS.

Conclusion

I am happy the issue ended that way.
  • The license of my CCID driver is respected by this Feitian driver
  • One more Free Software smart card reader driver is available for users

Saturday, December 13, 2014

PySCard 1.6.16 released

As explained in "PySCard: unofficial version 1.6.16 available" the PySCard software was no more actively maintained. After discussing with Jean-Daniel Aussel, previous maintainer, I got the admin rights to manage the sourceforge project. I was already an active code contributor since 2007.


Release 1.6.16

I just released a new official pyscard version 1.6.16.

Changes:
1.6.16 (December 2014)
  • added support for windows 64bit amd64 (Jean-Daniel Aussel)
  • support python "new" classes (derive classes from object) (Ludovic Rousseau from chrysn feature request ID 3110077)
  • fixed Reader.__eq__() (Ludovic Rousseau from Bernard Paulus bug ID 3418113)
  • fixed extended APDU transmit buffer too short by 2 (Jean-Daniel Aussel from bugs ID 2914636 and 3106761)
  • make Card and Reader objects hashable (Jean-Daniel Aussel from Hans-Peter Jansen feature request and patch)
  • convert the user guide to python-Sphinx

Documentation in Sphinx

I also changed the project web pages to use Sphinx (Python document generator) and generate a nice and modern user's guide from ReStructured text instead of manually edited HTML pages.

The API documentation is still using epydoc. Maybe I will switch it to Sphinx if I find the time and motivation. Help appreciated.

Provided software

The source code archive pyscard-1.6.16.tar.gz is of course provided.

I also provide a binary installer pyscard-1.6.16.macosx-10.9-x86_64.tar.gz for Mac OS X 10.9 and Python 2.7.

I do not use Windows so no installer is provided for this platform. Help appreciated.

Debian packages and Ubuntu packages are already provided in Debian and Ubuntu. The other GNU distribution should also provide packages but I have not checked.