Tuesday, July 7, 2015

OS X Yosemite bug: SCardConnect blocks in SCARD_SHARE_SHARED mode

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

SCardConnect(..., SCARD_SHARE_SHARED, ...)

SCardConnect() do not work correctly on Yosemite in a multi application context.

SCardConnect(..., SCARD_SHARE_SHARED, ...) will block its execution until SCardDisconnect() is called in the other application using the card or the card is removed.

The SCARD_SHARE_SHARED flag indicates that the connection shall be shared by different applications. Different application should be able to use the card at the same time. An application can use SCardBeginTransaction()/SCardEndTransaction() to get a temporary exclusive access to the card.

This can be really problematic if an application is not correctly written and SCardDisconnect() is not called. The concurrent application would be blocked forever on SCardConnect().

When SCardConnect() is unblocked by a card removal it will return the error code SCARD_E_NO_SMARTCARD.

This bug is present in Yosemite version 10.10.4. I have not verified if the bug is also present in previous Yosemite versions. Maybe it is a bug introduced in 10.10.4?

See also

Apple bug report #21703315 "PC/SC SCardConnect() blocks in SCARD_SHARE_SHARED mode"

Sample code

Thanks to Mounir for the initial sample code.

The sample application does:
  1. wait for a card insertion
  2. SCardConnect() to the card
  3. sleep for 3 seconds
  4. SCardDisconnect() from the card
  5. go to step 1

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

SCARDCONTEXT hContext;
SCARD_READERSTATE state;

#define CHECK_ERROR(text) \
    if (err != SCARD_S_SUCCESS) \
        printf("\033[0;31m" text ": %s (0x%08x)\033[00m\n",pcsc_stringify_error(err),err); \
    else \
        timed_log(text ": OK\n");

#define CHECK_EXIT(text) \
    CHECK_ERROR(text) \
    if (err != SCARD_S_SUCCESS) return -1;

static void timed_log(const char *msg)
{
    static struct timeval old_tp;
    struct timeval tp, r;

    gettimeofday(&tp, NULL);

    r.tv_sec = tp.tv_sec - old_tp.tv_sec;
    r.tv_usec = tp.tv_usec - old_tp.tv_usec;
    if (r.tv_usec < 0)
    {
        r.tv_sec--;
        r.tv_usec += 1000000;
    }

    printf("%ld.%.6d %s", r.tv_sec, r.tv_usec, msg);
    old_tp = tp;
}

static int WaitForCardEvent(void)
{
    int insert = 0;

    DWORD err;
    while (1)
    {
        timed_log("Waiting for event...\n");
        err = SCardGetStatusChange(hContext, INFINITE, &state, 1);
        CHECK_EXIT("SCardGetStatusChange")

        timed_log("event detected\n");
        state.dwCurrentState = state.dwEventState;

        if (state.dwEventState & SCARD_STATE_PRESENT)
        {
            if (! (state.dwEventState & SCARD_STATE_MUTE))
            {
                timed_log("card inserted\n");
                if (insert)
                    return 1;
            }
            else
                timed_log("card is mute\n");
        }
        else
        {
            timed_log("card removed\n");
            insert = 1;
        }
    }

    return 0;
}

static int UseCard(const char *mszReaders)
{
    DWORD dwActiveProtocol;
    SCARDHANDLE hCard = 0;

    timed_log("calling SCardConnect\n");
    DWORD err = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED,
        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
    CHECK_EXIT("SCardConnect")
    timed_log("connected\n");

    sleep(3);

#if 1
    timed_log("calling SCardDisconnect\n");
    err = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
    CHECK_ERROR("SCardDisconnect")
#endif

    return 1;
}

int main(void)
{
    LPSTR mszReaders;
    DWORD err, cchReaders;

    err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    CHECK_EXIT("SCardEstablishContext")
    cchReaders = 0;

    err = SCardListReaders(hContext, NULL, NULL, &cchReaders);
    CHECK_EXIT("SCardListReaders")
    mszReaders = calloc(cchReaders, sizeof(char));
    if (!mszReaders)
    {
        printf("calloc\n");
        return -1;
    }
    err = SCardListReaders(hContext, NULL, mszReaders, &cchReaders);
    CHECK_EXIT("SCardListReaders")

    printf("Using Reader: %s\n", mszReaders);

    memset(&state, 0, sizeof state);
    state.szReader = mszReaders;
    err = SCardGetStatusChange(hContext, 0, &state, 1);
    CHECK_EXIT("SCardGetStatusChange")

    while (1)
    {
        WaitForCardEvent();

        UseCard(mszReaders);
    }

    SCardReleaseContext(hContext);


    return 0;
}

You need to open 2 terminal windows and run the application concurrently in the 2 terminals.

Result (on Yosemite)

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

Terminal 1

$ ./main
1436274783.304010 SCardEstablishContext: OK
0.000163 SCardListReaders: OK
0.000108 SCardListReaders: OK
Using Reader: Dell Dell Smart Card Reader Keyboard
0.000971 SCardGetStatusChange: OK
0.000004 Waiting for event...
0.000540 SCardGetStatusChange: OK
0.000005 event detected
0.000001 card removed
0.000001 Waiting for event...

42.709172 SCardGetStatusChange: OK
0.000010 event detected
0.000002 card inserted
0.000001 calling SCardConnect
0.042665 SCardConnect: OK
0.000010 connected
3.000132 calling SCardDisconnect
0.000339 SCardDisconnect: OK
0.000006 Waiting for event...
0.000825 SCardGetStatusChange: OK
0.000006 event detected
0.000001 card inserted
0.000001 Waiting for event...
3.001277 SCardGetStatusChange: OK
0.000008 event detected
0.000001 card inserted
0.000001 Waiting for event...

Terminal 2

$ ./main
1436274812.713724 SCardEstablishContext: OK
0.000145 SCardListReaders: OK
0.000091 SCardListReaders: OK
Using Reader: Dell Dell Smart Card Reader Keyboard
0.001038 SCardGetStatusChange: OK
0.000005 Waiting for event...
0.000717 SCardGetStatusChange: OK
0.000006 event detected
0.000001 card removed
0.000001 Waiting for event...

13.299278 SCardGetStatusChange: OK
0.000007 event detected
0.000001 card inserted
0.000001 calling SCardConnect
3.043707 SCardConnect: OK
0.000014 connected
3.000740 calling SCardDisconnect
0.000396 SCardDisconnect: OK
0.000007 Waiting for event...

Description

I added a newline in the traces just before the card insertion. The number in front of each line is the time that passed since the previous log line. So if you see "1.23 foo" then "foo" happened 1.23 second after the previous log.

In this execution the application in terminal 1 got the card connection in the first place. You can see that SCardConnect() returns after 0.042665 seconds. The state was "connected" for 3.000132 seconds. Then SCardDisconnect() is called.

In terminal 2 we note that SCardConnect() returns after 3.043707 seconds. The application is blocked during the 3 seconds used by the the first application.

Also note that in terminal 1 SCardGetStatusChange() returns after 3.001277 seconds. This is because the card was used by the application in terminal 2 and the card state changed. The bit SCARD_STATE_INUSE changed from 1 (card in use) to 0 (card not used).

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

Terminal 1

$ ./main1436274912.852172 SCardEstablishContext: OK
0.000144 SCardListReaders: OK
0.000134 SCardListReaders: OK
Using Reader: Gemalto PC Twin Reader (70D7E2EE) 00 00
0.000138 SCardGetStatusChange: OK
0.000012 Waiting for event...
0.000089 SCardGetStatusChange: OK
0.000020 event detected
0.000003 card removed
0.000002 Waiting for event...

11.780610 SCardGetStatusChange: OK
0.000014 event detected
0.000002 card inserted
0.000002 calling SCardConnect
0.033486 SCardConnect: OK
0.000013 connected
3.000073 calling SCardDisconnect
0.000125 SCardDisconnect: OK
0.000016 Waiting for event...

Terminal 2

$ ./main 
1436274916.659394 SCardEstablishContext: OK
0.000163 SCardListReaders: OK
0.000149 SCardListReaders: OK
Using Reader: Gemalto PC Twin Reader (70D7E2EE) 00 00
0.000144 SCardGetStatusChange: OK
0.000033 Waiting for event...
0.000108 SCardGetStatusChange: OK
0.000023 event detected
0.000019 card removed
0.000007 Waiting for event...

7.973306 SCardGetStatusChange: OK
0.000025 event detected
0.000009 card inserted
0.000007 calling SCardConnect
0.033451 SCardConnect: OK
0.000028 connected
3.000083 calling SCardDisconnect
0.000215 SCardDisconnect: OK
0.000025 Waiting for event...

Description

On Linux you can see that the 2 applications run at the same time concurrently. No application is blocked by the other.

Known workaround

None known.

Some ideas that should help:
  • Do not forget to call SCardDisconnect() when you no longer use the card, or another application may be blocked for a long time.
  • Connect to a card for short periods of time if possible. Another application would then get a chance to access the card.

Saturday, June 27, 2015

PySCard 1.7.0 released

Release 1.7.0

I just released a new official pyscard version 1.7.0 of pyscard. pyscard is a python module adding smart cards support (PC/SC) to python.

Changes:

1.7.0 (June 2015)
  • PCSCCardConnection: Fix a problem with mode=SCARD_SHARE_DIRECT
  • add support of cygwin as a build platform
  • Fix a problem with Windows Remote Desktop
  • Switch from distutils to setuptools
  • dropped support for Python 2.5 and earlier (Alex Willmer)
  • dropped support for OS X 10.5 (Leopard) and earlier (Alex Willmer)
  • minor bugs fixed

Provided software

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

I also provide a binary installer pyscard-1.7.0.macosx-10.10-intel.tar.gz for Mac OS X 10.10 Yosemite and Python 2.7.

Continuous Integration

The PySCard project now uses two continuous integration platforms:

Windows installers?

Maybe AppVeyor can be used to provide binary installers for Windows. Your help is welcome on this task because I am not a Windows user.

Friday, June 19, 2015

PCSC framework will stay in Mac OS X 10.11 El Capitan



In a previous article "PCSC framework removed in Mac OS X 10.11 El Capitan?" I was afraid that the PCSC framework would be removed in the next version of Mac OS X: El Capitan.

I learned that this will not the be case. It was a misinterpretation on my side.

PCSC framework

On the page "OS X v.10.11 API Diffs" PCSC is removed in the 'Modules' section but is not removed in the 'Framework' section. The PCSC framework is still provided and supported in El Capitan.

This means that PCSC.framework public API will not changed at all and is perfectly usable from C/C++/ObjC applications.

Swift

For developing smart card applications with Swift, CryptoTokenKit framework/module is recommended by Apple.

The effect that PCSC Module is removed is that it will not be possible to import PCSC in a Swift application on El Capitan.

On Yosemite you can import PCSC but no SCard functions nor declarations are actually visible and nothing could be used, the import is in fact broken. So using El Capitan should not break existing Swift application.

Maybe someone will write a PC/SC wrapper for Swift so I can add it to my list "PC/SC sample in different languages".

Conclusion

PCSC framework will be available in Mac OS X 10.11 El Capitan. No need to stress and rewrite/port all existing PC/SC applications to CryptoTokenKit.

I "guess" Apple will bring other surprises on the PC/SC front in El Capitan (but I can't tell now). If you have tried the beta version you may already know :-).

Thursday, June 18, 2015

OS X Yosemite bug: SCardGetStatusChange blocks forever

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

SCardGetStatusChange() blocks forever after some time

SCardGetStatusChange() does not work correctly on Mac OS X 10.10 Yosemite. Sometimes the function blocks and does not return.

The execution do not always block at the same iteration. It looks like an internal dead lock in some circumstances.

See also

The problem was reported to me by Paolo S. Thanks to him.

Apple bug report #21437286 "PC/SC SCardGetStatusChange() blocks forever after some time".
Closed by Apple on 27th June 2015, as duplicate of  #20517200.

Sample code

Code from Paolo S.

#include <stdio.h>
#include <stdlib.h>

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

#define CHECK(f, rv) \
 if (SCARD_S_SUCCESS != rv) \
 { \
  printf(f ": %s\n", pcsc_stringify_error(rv)); \
  return -1; \
 }

int main(void) {
    LONG rv;

    SCARDCONTEXT hContext;
    LPTSTR mszReaders;
    DWORD dwReaders;

    unsigned int i;

    rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
    CHECK("SCardEstablishContext", rv)

    rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
    CHECK("SCardListReaders", rv)

    mszReaders = calloc(dwReaders, sizeof(char));
    rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
    CHECK("SCardListReaders", rv)

    if (dwReaders <= 1) {
        puts("No reader found");
        goto end ;
    }

    printf("reader name: %s\n", mszReaders);

    SCARD_READERSTATE sReaderState ;
    sReaderState.szReader = mszReaders ;

    i = 0 ;
    while(1) {
        i++ ;
        printf("Loop #%d: calling SCardGetStatusChange\n", i);
        rv = SCardGetStatusChange(hContext, 1, &sReaderState, 1);   // This hangs after random number of calls
        puts("returned");
        CHECK("SCardGetStatusChange", rv)
    }

    free(mszReaders);

end:
    rv = SCardReleaseContext(hContext);

    CHECK("SCardReleaseContext", rv)

    return 0;
}

Result (on Yosemite)

$ ./main
reader name: Gemalto PC Twin Reader
Loop #1: calling SCardGetStatusChange
returned
Loop #2: calling SCardGetStatusChange
[...]
Loop #11145: calling SCardGetStatusChange
returned
Loop #11146: calling SCardGetStatusChange
^C

I have to use Control-C to stop the execution.

Known workaround

Paolo S. has a workaround.
  1. detect the card presence by calling the SCardConnect() function (shared mode, default protocols). no errors = card inserted.
  2. detect the card extraction by calling the SCardReconnect() function (same parameters, no card reset or eject). no errors = card still inserted.

Wednesday, June 10, 2015

PCSC framework removed in Mac OS X 10.11 El Capitan?

OS X El Capitan

You may know that Apple announced the new major version of OS during this week Apple WWDC 2015 (Worldwide Developers Conference). The next major version of OS X is 10.11, is planned for this Fall and is called El Capitan. See OS X El Capitan for some more details.

For even more details you have to go to the (public) web page "OS X v.10.11 API Diffs".

PCSC framework

According to the (public) web page "OS X v.10.11 API Diffs" we have:

It looks like Apple decided to remove the PCSC framework and replace it with CryptoTokenKit, already introduced in OS X 10.10 Yosemite (see "OS X Yosemite BETA and smart cards status" and "OS X Yosemite and smart cards status" for the beginning of the replacement story).

CryptoTokenKit

We also have some changes (to make it work for real?) in the CryptoTokenKit side (both framework and modules).

CryptoTokenKit framework

Full page at https://developer.apple.com/library/prerelease/mac/releasenotes/General/APIDiffsMacOSX10_11/frameworks/CryptoTokenKit.html

TKError.h

Added TKErrorCodeBadParameter

TKSmartCard.h

Removed TKSmartCardNoSlot
Removed TKSmartCardSlotEmpty
Removed TKSmartCardSlotMuteCard
Removed TKSmartCardSlotProbing
Removed TKSmartCardSlotValidCard
Added -[TKSmartCard userInteractionForSecurePINChangeWithPINFormat:APDU:currentPINByteOffset:newPINByteOffset:]
Added -[TKSmartCard userInteractionForSecurePINVerificationWithPINFormat:APDU:PINByteOffset:]
Added TKSmartCardPINFormat
Added TKSmartCardPINFormat.charset
Added TKSmartCardPINFormat.encoding
Added TKSmartCardPINFormat.maxPINLength
Added TKSmartCardPINFormat.minPINLength
Added TKSmartCardPINFormat.PINBitOffset
Added TKSmartCardPINFormat.PINBlockByteLength
Added TKSmartCardPINFormat.PINJustification
Added TKSmartCardPINFormat.PINLengthBitOffset
Added TKSmartCardPINFormat.PINLengthBitSize
Added TKSmartCardSlot.screen
Added -[TKSmartCardSlot userInteractionForConfirmation]
Added -[TKSmartCardSlot userInteractionForStringEntry]
Added TKSmartCardSlotScreen
Added -[TKSmartCardSlotScreen displayMessage:x:y:duration:clearScreen:]
Added TKSmartCardSlotScreen.physicalColumnCount
Added TKSmartCardSlotScreen.physicalRowCount
Added TKSmartCardSlotScreen.virtualColumnCount
Added TKSmartCardSlotScreen.virtualRowCount
Added TKSmartCardUserInteraction
Added -[TKSmartCardUserInteraction cancel]
Added TKSmartCardUserInteraction.delegate
Added TKSmartCardUserInteraction.initialTimeout
Added TKSmartCardUserInteraction.interactionTimeout
Added -[TKSmartCardUserInteraction runWithReply:]
Added TKSmartCardUserInteractionDelegate
Added -[TKSmartCardUserInteractionDelegate characterEnteredInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate correctionKeyPressedInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate invalidCharacterEnteredInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate newPINConfirmationRequestedInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate newPINRequestedInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate oldPINRequestedInUserInteraction:]
Added -[TKSmartCardUserInteractionDelegate validationKeyPressedInUserInteraction:]
Added TKSmartCardUserInteractionForConfirmation
Added TKSmartCardUserInteractionForConfirmation.result
Added TKSmartCardUserInteractionForPINOperation
Added TKSmartCardUserInteractionForPINOperation.locale
Added TKSmartCardUserInteractionForPINOperation.PINCompletion
Added TKSmartCardUserInteractionForPINOperation.PINMessageIndices
Added TKSmartCardUserInteractionForPINOperation.resultData
Added TKSmartCardUserInteractionForPINOperation.resultSW
Added TKSmartCardUserInteractionForSecurePINChange
Added TKSmartCardUserInteractionForSecurePINChange.PINConfirmation
Added TKSmartCardUserInteractionForSecurePINVerification
Added TKSmartCardUserInteractionForStringEntry
Added TKSmartCardUserInteractionForStringEntry.result
Added TKSmartCardPINCharset
Added TKSmartCardPINCharsetAlphanumeric
Added TKSmartCardPINCharsetNumeric
Added TKSmartCardPINCharsetUpperAlphanumeric
Added TKSmartCardPINCompletion
Added TKSmartCardPINCompletionKey
Added TKSmartCardPINCompletionMaxLength
Added TKSmartCardPINCompletionTimeout
Added TKSmartCardPINConfirmation
Added TKSmartCardPINConfirmationCurrent
Added TKSmartCardPINConfirmationNew
Added TKSmartCardPINConfirmationNone
Added TKSmartCardPINEncoding
Added TKSmartCardPINEncodingASCII
Added TKSmartCardPINEncodingBCD
Added TKSmartCardPINEncodingBinary
Added TKSmartCardPINJustification
Added TKSmartCardPINJustificationLeft
Added TKSmartCardPINJustificationRight

and many functions modified.

CryptoTokenKit modules

Full page at https://developer.apple.com/library/prerelease/mac/releasenotes/General/APIDiffsMacOSX10_11/modules/CryptoTokenKit.html

Removed TKSmartCardProtocol.init(_: UInt)
Added TKErrorCode.CodeBadParameter
Added TKSmartCard.userInteractionForSecurePINChangeWithPINFormat(_: TKSmartCardPINFormat, APDU: NSData, currentPINByteOffset: Int, newPINByteOffset: Int) -> TKSmartCardUserInteractionForSecurePINChange?
Added TKSmartCard.userInteractionForSecurePINVerificationWithPINFormat(_: TKSmartCardPINFormat, APDU: NSData, PINByteOffset: Int) -> TKSmartCardUserInteractionForSecurePINVerification?
Added TKSmartCardPINCharset [enum]
Added TKSmartCardPINCharset.Alphanumeric
Added TKSmartCardPINCharset.Numeric
Added TKSmartCardPINCharset.UpperAlphanumeric
Added TKSmartCardPINCompletion [struct]
Added TKSmartCardPINCompletion.init(rawValue: UInt)
Added TKSmartCardPINCompletion.Key
Added TKSmartCardPINCompletion.MaxLength
Added TKSmartCardPINCompletion.Timeout
Added TKSmartCardPINConfirmation [struct]
Added TKSmartCardPINConfirmation.Current
Added TKSmartCardPINConfirmation.init(rawValue: UInt)
Added TKSmartCardPINConfirmation.New
Added TKSmartCardPINConfirmation.None
Added TKSmartCardPINEncoding [enum]
Added TKSmartCardPINEncoding.ASCII
Added TKSmartCardPINEncoding.BCD
Added TKSmartCardPINEncoding.Binary
Added TKSmartCardPINFormat
Added TKSmartCardPINFormat.charset
Added TKSmartCardPINFormat.encoding
Added TKSmartCardPINFormat.maxPINLength
Added TKSmartCardPINFormat.minPINLength
Added TKSmartCardPINFormat.PINBitOffset
Added TKSmartCardPINFormat.PINBlockByteLength
Added TKSmartCardPINFormat.PINJustification
Added TKSmartCardPINFormat.PINLengthBitOffset
Added TKSmartCardPINFormat.PINLengthBitSize
Added TKSmartCardPINJustification [enum]
Added TKSmartCardPINJustification.Left
Added TKSmartCardPINJustification.Right
Added TKSmartCardSlot.screen
Added TKSmartCardSlot.userInteractionForConfirmation() -> TKSmartCardUserInteractionForConfirmation?
Added TKSmartCardSlot.userInteractionForStringEntry() -> TKSmartCardUserInteractionForStringEntry?
Added TKSmartCardSlotScreen
Added TKSmartCardSlotScreen.displayMessage(_: String, x: Int, y: Int, duration: NSTimeInterval, clearScreen: Bool) -> Bool
Added TKSmartCardSlotScreen.physicalColumnCount
Added TKSmartCardSlotScreen.physicalRowCount
Added TKSmartCardSlotScreen.virtualColumnCount
Added TKSmartCardSlotScreen.virtualRowCount
Added TKSmartCardUserInteraction
Added TKSmartCardUserInteraction.cancel() -> Bool
Added TKSmartCardUserInteraction.delegate
Added TKSmartCardUserInteraction.initialTimeout
Added TKSmartCardUserInteraction.interactionTimeout
Added TKSmartCardUserInteraction.runWithReply(_: (Bool, NSError?) -> Void)
Added TKSmartCardUserInteractionDelegate
Added TKSmartCardUserInteractionDelegate.characterEnteredInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.correctionKeyPressedInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.invalidCharacterEnteredInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.newPINConfirmationRequestedInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.newPINRequestedInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.oldPINRequestedInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionDelegate.validationKeyPressedInUserInteraction(_: TKSmartCardUserInteraction)
Added TKSmartCardUserInteractionForConfirmation
Added TKSmartCardUserInteractionForConfirmation.result
Added TKSmartCardUserInteractionForPINOperation
Added TKSmartCardUserInteractionForPINOperation.locale
Added TKSmartCardUserInteractionForPINOperation.PINCompletion
Added TKSmartCardUserInteractionForPINOperation.PINMessageIndices
Added TKSmartCardUserInteractionForPINOperation.resultData
Added TKSmartCardUserInteractionForPINOperation.resultSW
Added TKSmartCardUserInteractionForSecurePINChange
Added TKSmartCardUserInteractionForSecurePINChange.PINConfirmation
Added TKSmartCardUserInteractionForSecurePINVerification
Added TKSmartCardUserInteractionForStringEntry
Added TKSmartCardUserInteractionForStringEntry.result

and many functions modified.

WWDC sessions

Apple provides 2 security related sessions at the WWDC according to the schedule.

Security and Your Apps
System Frameworks
Your reputation depends on protecting your users' data. Do this by taking advantage of the Security frameworks built into iOS, OS X and watchOS. Find out about new developments in Security and learn the best practices to develop secure apps and protect your users' data.

Security and Privacy Lab
System Frameworks
Get together with the Apple security and privacy teams for expert advice on protecting your apps, safeguarding your users' data, and honoring your users' privacy. Bring your code and your questions.
It may be interesting to watch the videos of the sessions once they are available. Or may they are not related to smart card at all.

Conclusion

PC/SC application developers, maintainers and vendors will have some work to port/rewrite the PC/SC code to CryptoTokenKit. And the delay is very short to be ready for the release of OS X El Capitan (in 3 months ?).

Thanks to Karl T. for the notice.

Update

PCSC framework will be present in El Capitan. See "PCSC framework will stay in Mac OS X 10.11 El Capitan".

Wednesday, May 27, 2015

PCSC sample in C for UEFI

To continue the list of PC/SC wrappers initiated more than four years ago with "PC/SC sample in different languages" I now present a PC/SC sample written in C but for UEFI.

UEFI

UEFI stands for Unified Extensible Firmware Interface. UEFI is an evolution of EFI. EFI should replace BIOS on "modern" "Intel PC" computers.

Any 64-bits Intel (or compatible) PC computer should have UEFI (and maybe also a BIOS for compatibility).

See my previous article "UEFI Smart Card Reader Protocol" for some more details.

Sample source code

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/SmartCardReader.h>

int HelloWorld(EFI_SMART_CARD_READER_PROTOCOL *SmartCardReader)
{
    EFI_STATUS  Status;
    UINT32 ActiveProtocol;
    int i;
    UINT8 CAPDU_select[] = {0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01};
    UINT8 CAPDU_command[] = {0x00, 0x00, 0x00, 0x00};
    UINTN CAPDULength, RAPDULength;
    UINT8 RAPDU[256+2];

    /*
     * SCardConnect
     */
    Status = SmartCardReader->SCardConnect(SmartCardReader,
        SCARD_AM_CARD,
        SCARD_CA_COLDRESET,
        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
        &ActiveProtocol);
    if (EFI_ERROR(Status))
    {
        Print(L"ERROR: SCardConnect: %d\n", Status);
        return 0;
    }

    /*
     * SCardTransmit Select
     */
    CAPDULength = sizeof CAPDU_select;
    RAPDULength = sizeof RAPDU;
    Status = SmartCardReader->SCardTransmit(SmartCardReader,
        CAPDU_select, CAPDULength,
        RAPDU, &RAPDULength);
    if (EFI_ERROR(Status))
    {
        Print(L"ERROR: SCardTransmit: %d\n", Status);
        return 0;
    }
    Print(L"RAPDU: ");
    for (i=0; i<RAPDULength; i++)
        Print(L"%02X ", RAPDU[i]);
    Print(L"\n");

    /*
     * SCardTransmit Command
     */
    CAPDULength = sizeof CAPDU_command;
    RAPDULength = sizeof RAPDU;
    Status = SmartCardReader->SCardTransmit(SmartCardReader,
        CAPDU_command, CAPDULength,
        RAPDU, &RAPDULength);
    if (EFI_ERROR(Status))
    {
        Print(L"ERROR: SCardTransmit: %d\n", Status);
        return 0;
    }
    Print(L"RAPDU: ");
    for (i=0; i<RAPDULength; i++)
        Print(L"%02X ", RAPDU[i]);
    Print(L"\n");
    for (i=0; i<RAPDULength; i++)
        Print(L"%c", RAPDU[i]);
    Print(L"\n");

    /*
     * SCardDisconnect
     */
    Status = SmartCardReader->SCardDisconnect(SmartCardReader,
        SCARD_CA_NORESET);
    if (EFI_ERROR(Status))
    {
        Print(L"ERROR: SCardDisconnect: %d\n", Status);
        return 0;
    }

    return 0;
}

INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
    EFI_STATUS  Status;
    UINTN       HandleIndex, HandleCount;
    EFI_HANDLE  *DevicePathHandleBuffer = NULL;
    EFI_SMART_CARD_READER_PROTOCOL *SmartCardReader;

    /* EFI_SMART_CARD_READER_PROTOCOL */
    Status = gBS->LocateHandleBuffer(
            ByProtocol,
            &gEfiSmartCardReaderProtocolGuid,
            NULL,
            &HandleCount,
            &DevicePathHandleBuffer);

    if (EFI_ERROR(Status))
    {
        Print(L"ERROR: Get EFI_SMART_CARD_READER_PROTOCOL count fail.\n");
        return 0;
    }

    Print(L"Found %d reader(s)\n", HandleCount);
    for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++)
    {
        ZeroMem(&SmartCardReader, sizeof SmartCardReader);

        Status = gBS->HandleProtocol(
                DevicePathHandleBuffer[HandleIndex],
                &gEfiSmartCardReaderProtocolGuid,
                (VOID**)&SmartCardReader);

        if (EFI_ERROR(Status))
        {
            Print(L"ERROR: Open Protocol fail.\n");
            gBS->FreePool(DevicePathHandleBuffer);
            return 0;
        }

        HelloWorld(SmartCardReader);
    }
    gBS->FreePool(DevicePathHandleBuffer);

    return(0);
}

Output

Found 1 reader(s)
RAPDU: 90 00 
RAPDU: 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00 
Hello world!

Test platform

I used a Dell laptop model E6420 (shipped in August 2013) with an integrated Broadcom smart card reader. This reader was already in the "should work" list of my CCID driver. Of course the SmartCardReader API was not included in the UEFI. I had to write the driver myself.

I made tests with older Dell laptops and had problems with the USB UEFI layer. It looks like UEFI is still a work in progress and bugs/limitations are common.

I started by using a software simulator qemu + UEFI but I could not use a USB device with the simulator. So I rapidly had to use a real hardware. Developing a UEFI driver involves a lot of reboot to try a new version of the driver.

Remarks

I added error checks and logging feature in the source code.

The API is not high level but mostly the direct equivalent of WinSCard (the classic C API for smart cards).

Conclusion

It was fun to work on UEFI and learn a new technology.

UEFI is very powerful. Time will tell if the SmartCardReader API will be deployed and used.

UEFI Smart Card Reader Protocol

The release 2.5, April 2015 of the Unified Extensible Firmware Interface Specification (UEFI) contains 2 new protocols:
  • Smart Card Reader Protocol
  • Smart Card Edge Protocol

The specification is available in at http://www.uefi.org/sites/default/files/resources/UEFI%202_5.pdf from the UEFI web site.

Smart Card Reader Protocol

The Smart Card Reader Protocol is described in chapter 35.6.1 page 2241 (yes, the specification is one huge document of 14 MB and 2588 pages).

The functions provided are:
typedef struct _EFI_SMART_CARD_READER_PROTOCOL {
 EFI_SMART_CARD_READER_CONNECT    SCardConnect;
 EFI_SMART_CARD_READER_DISCONNECT SCardDisconnect;
 EFI_SMART_CARD_READER_STATUS     SCardStatus;
 EFI_SMART_CARD_READER_TRANSMIT   SCardTransmit;
 EFI_SMART_CARD_READER_CONTROL    SCardControl;
 EFI_SMART_CARD_READER_GET_ATTRIB SCardGetAttrib;
} EFI_SMART_CARD_READER_PROTOCOL;

You may be surprised that there is no function to list the available readers. This is because UEFI has its own way to enumerate resources. Each smart card reader will have its own EFI_SMART_CARD_READER_PROTOCOL structure. The program just have to iterate over all the protocols identified as EFI_SMART_CARD_READER_PROTOCOL_GUID.

Usage

The planned usage of the Smart Card Reader Protocol is to be used from an UEFI application so before the operating system (Windows, GNU/Linux, Mac OS X, etc.) is started.
This can be used to access a smart card and get a secret key from the smart card after a PIN has been verified. The secret key could be used to decipher the hard disk.

Smart Card Edge Protocol

The Smart Card Edge Protocol is described in chapter 35.6.2 page 2253.

The functions provided are:
typedef struct _EFI_SMART_CARD_EDGE_PROTOCOL {
 EFI_SMART_CARD_EDGE_GET_CONTEXT        GetContext;
 EFI_SMART_CARD_EDGE_CONNECT            Connect;
 EFI_SMART_CARD_EDGE_DISCONNECT         Disconnect;
 EFI_SMART_CARD_EDGE_GET_CSN            GetCsn;
 EFI_SMART_CARD_EDGE_GET_READER_NAME    GetReaderName;
 EFI_SMART_CARD_EDGE_VERIFY_PIN         VerifyPin;
 EFI_SMART_CARD_EDGE_GET_PIN_REMAINING  GetPinRemaining;
 EFI_SMART_CARD_EDGE_GET_DATA           GetData;
 EFI_SMART_CARD_EDGE_GET_CREDENTIAL     GetCredential;
 EFI_SMART_CARD_EDGE_SIGN_DATA          SignData;
 EFI_SMART_CARD_EDGE_DECRYPT_DATA       DecryptData;
 EFI_SMART_CARD_EDGE_BUILD_DH_AGREEMENT BuildDHAgreement;
} EFI_SMART_CARD_EDGE_PROTOCOL;

Usage

This API allows to easily use a PKI card. It is the same idea as a PKCS#11 or MiniDriver library: abstract the smart card specificities and make an UEFI application able to use different PKI smart cards without writing specific source code.

Can I use it now?

The specification is now public. You can implement it yourself and play with it. Or you can wait for someone else to implement it and provide it in the UEFI of your next computer.

I already implemented the Smart Card Reader Protocol. I will proposed it for inclusion to TianoCore.

Conclusion

Stay tuned. Do not expect to have it included in the UEFI of your next computer before some time.

But if you are a developer you can play with it now.