Smart Storage

Protect Your Data Via Managed Code And The Windows Vista Smart Card APIs

Dan Griffin

This article is based on a prerelease version of Windows Vista. All information contained herein is subject to change.

This article discusses:

  • Windows smart card programming basics
  • Road map for a sample smart card app
  • Writing managed wrappers for smart card functionality
  • Transaction management for smart cards
This article uses the following technologies:
Windows Vista, C++, C#

Code download available at:SmartCards2006_11.exe(2079 KB)

Contents

Windows Smart Card Programming
Smart Card Evolution
Sample App Roadmap
WinSCard API Wrapper
GetSmartCard Helper Routine
Card Module API Wrappers
Handling CardAcquireContext
Transaction Management
Card Module Interface Design Rationale
Using CLR Crypto
Dependencies and Testing

The smart card concept—basically, a credit card with an embedded microchip—is around 30 years old. But today's emphasis on security is driving companies and governments alike to take a fresh look at some old ideas.

Smart cards are a compelling alternative to the weak link of authentication systems: passwords. The industry is hungry for password-replacement technologies. Via an embedded cryptographic processor, smart cards provide a very secure and easy-to-use authentication mechanism.

However, smart card deployments introduce their own challenges. The industry needs better products for deployment and management of complex authentication technologies. In his keynote address at the RSA 2006 conference, Bill Gates demonstrated Microsoft Certificate Lifecycle Manager, a product that takes advantage of the APIs discussed in this article.

The industry is also beginning to respond to consumer privacy-protection demands. Consumers need a way to protect privileged information; for example, online banking in the US will soon require strong authentication. And down the road, consumers will use technologies such as Windows® CardSpace to select what personal information they reveal in a variety of online transactions. For example, during online banking, I may cryptographically prove that my Social Security Number (SSN) is bound to my identity, but there's no reason for me to share my credit card number. In contrast, I'll authorize my credit card number, but not my SSN, to be securely shared with an e-commerce site.

Microsoft has recognized the pivotal role of smart cards in its platform security strategy. Developers need to understand how smart card-aware applications work and what facilities the Windows operating system exposes to make life easier.

Windows Smart Card Programming

Windows has supported the Personal Computer/Smart Card (PC/SC) software stack for about a decade (see www.pcscworkgroup.com for more information). PC/SC functionality is exposed to applications via the Windows Smart Card (WinSCard) client API, implemented in winscard.dll and, to a lesser degree, scarddlg.dll. The client API allows host applications to communicate with smart cards indirectly via the Smart Card Resource Manager service, also known as SCardSvr.

The resource manager model is used because the smart card is a shared resource, and multiple applications may attempt to communicate with the card at any given time. Communication with the card must be serialized. Therefore, a simplified, database-like locking model is enforced by the resource manager. In PC/SC, holding the lock for a given smart card reader (also known as an Interface Device or IFD) is known as holding the transaction. The term "transaction" in this context causes some confusion, though. For example, there's no such thing as rollback or de-commit; PC/SC can't automatically undo changes made by the application within a transaction.

An alternate description of PC/SC can be found in the Platform SDK documentation for SCardEstablishContext, SCardBeginTransaction, and other WinSCard functions. A host application uses these routines to establish a pipe to the resource manager, obtain an exclusive lock on a smart card reader, send a sequence of commands to the card, and release the lock.

The PC/SC component stack is summarized in Figure 1. Note that the PC/SC client code is loaded in the application process. The resource manager is a system service, separate from the client. The reader device driver is the only component of the PC/SC stack that operates in kernel mode. The reader drivers are loaded exclusively by the resource manager to prevent applications from bypassing the transaction model. That's a critical aspect of the PC/SC security model. Finally, the APIs discussed in this article are available only to user mode callers. Unfortunately, there's no kernel mode resource manager.

Figure 1 PC/SC Component Stack

Figure 1** PC/SC Component Stack **

The rest of the article will focus on the top portion of the stack: the host application and the PC/SC client API. I'll next discuss the limitations of this programming model and how it's evolving.

Smart Card Evolution

To date, the typical uses for smart cards on Windows have been authentication-related. For example, they can be used instead of a password to log on. These smart card authentication schemes are cryptography-based. As a result, adding support for a new smart card in Windows has historically required vendors to implement a plug-in known as a CSP (Cryptographic Service Provider), which implements the interface known as Crypto API 1.0. Unfortunately, CSPs are notoriously complicated to write. Furthermore, since the CSP interface was intended only for low-level cryptographic operations, it is not capable of exposing the rich new features of modern smart cards. These features include increased data storage capacity, high-bandwidth I/O, on-card applets, and new crypto algorithms. The lack of an application-level programming model for those capabilities has been a thorn in the side of hardware vendors looking for differentiation in the market and for programmers who want to write for as wide a variety of hardware as possible.

Thankfully, the situation is greatly improved in Windows Vista™ due to a new plug-in model known as the Card Module API. The great thing about the new API is that it allows smart-card-aware applications to be card-type-agnostic. Let's look at an example.

My sample is a Windows digital rights management (DRM) application that uses a smart card to store a digital media license. The digital license consists of a digitally signed, compressed XML blob. In interacting with the smart card over the lifetime of the license, the host application executes some simple commands on the card: create a file to store the license, write the file, read the file, and delete the file. Each command is equivalent to a different sequence of bytes. For example, if the application determines that the file known to the smart card as "02" (many smart cards have very simple file systems) contains the license blob, then a relevant command/byte-sequence could encode: "read the first 128 bytes of file 02." Each command is sent to the card via the WinSCard function SCardTransmit. I'll come back to that in a second.

One problem in generalizing a smart-card-aware application is that different vendors' cards use different command encodings. Granted, there has been some standardization, such as ISO 7816, and the ISO encoding for a basic operation such as reading a file may indeed work on a wide variety of cards. But generally this is not something that developers can count on. Hence, the DRM application in this example would have to be modified to support each new smart card type. In my experience, the application code that builds those smart card command byte sequences is messy and tough to maintain.

The new card module API addresses this card incompatibility issue by providing a familiar file-system-like interface, plus some additional routines to expose the crypto-related authentication requirements mentioned earlier. For example, if the application is using the old model of including per-card encodings for the command "read the first 128 bytes of file 02," the pseudocode might look something like Figure 2.

Figure 2 Pseudocode for Per-Card-Type Command Handling

if (cardType == A) { // Build read-file transmit buffer for card type A ... // Send the command to the card SCardTransmit( cardHandle, // PC/SC context handle pciInputPointerA, // Protocol Control Information (in) transmitBufferA, // Our read-file command transmitBufferLength,// Read-file command length pciOutputPointerA, // Protocol Control Information (out) receiveBuffer, // Data response from card receiveBufferLength);// Length of data response // Interpret the response from this card type ... } else if (cardType == B) { // Build read-file transmit buffer for card type A ... // Send the command to the card SCardTransmit( cardHandle, // PC/SC context handle pciInputPointerB, // Protocol Control Information (in) transmitBufferB, // Our read-file command transmitBufferLength,// Read-file command length pciOutputPointerB, // Protocol Control Information (out) receiveBuffer, // Data response from card receiveBufferLength);// Length of data response // Interpret the response from this card type ... }

In contrast, by using the new API, I can simply replace the whole block with a single function call:

CardReadFile( contextPointer, // PC/SC context handles NULL, // Directory in which requested file is located "license", // File name to read &dataOut, // Contents of the requested file &dataLengthOut); // Size of the file contents

If you're thinking that the new API looks much simpler than the old way, I agree. But don't worry; there's still some work to do!

So far, I've described some of the basics of the PC/SC software stack and the card module API. I'm almost ready to tackle the sample code of the DRM application. Since none of the smart-card-related interfaces I've described are exposed to Microsoft® .NET Framework code, wouldn't it be cool if I could combine the power of the card module API with the convenience of .NET?

I figured that the most generally accessible solution would be to provide code wrappers that exercise the necessary WinSCard and Card Module routines via P/Invoke (a mechanism for calling native DLL exports from managed code). Then I could demonstrate the whole application in managed code. I found out it would be easier to write some additional native wrapper code to simplify this task. In retrospect, implementing the managed interfaces without detailed knowledge of the native API would have been quite difficult.

I'll first outline what the sample application needs to do. Then I'll discuss the wrappers and P/Invoke interfaces in detail.

Sample App Roadmap

First, the application will locate and bind to an inserted smart card. If no smart card is inserted, the user will be prompted to insert one. These operations require the WinSCard API, so this portion of the application will depend heavily on P/Invoke and the native wrappers.

Next, I'll open a handle to a cryptographic key pair on the card. If a suitable existing key pair isn't available, I'll try to create a new one. Fortunately, the required crypto-related functionality is already exposed by the CLR, so this is easy. There is one potential hurdle for the crypto portion of the application: the new Base Smart Card Crypto Service Provider must be installed since it's the most reliable way to interface with card module-based cards.

I'll create some sample XML data to represent a digital media license. Then I'll encrypt that data with the crypto key pair. Again, the required XML and crypto-related functionality are already available to use via the CLR. I'll save the data and signature to the card via the card module. This will require new native wrappers and P/Invoke interfaces for the native routines. Next, since this application is intended to be a test, I'll do the reverse of the previous steps. That is, I'll read back the file I just wrote to the card. Finally, I'll decrypt the data via the CLR crypto routines.

Now that I've summarized the behavior I'd like to see in the application, let's categorize the functional requirements. These three building blocks are all I need: wrappers for the WinSCard API, wrappers for the Card Module API, and the existing CLR crypto routines. Once those are implemented and understood, the writing of the application itself is easy! Let's look at each building block in detail. I recommend that you download the sample C# code to follow along, although the text is peppered with comments about smart card programming fundamentals in order to keep things interesting.

WinSCard API Wrapper

The first thing the application does is call into the .NET Crypto API to find a suitable key pair. However, I'm going to discuss that portion after describing the lower-level smart card API calls that the application makes, in order to make a point about the overall architecture.

To interact directly with the smart card via the card module API, the first thing I need to do is acquire PC/SC handles for a suitable card. (Please note that each of the native routines referenced here is documented in the Platform SDK, so I won't provide detailed usage for every public routine that's consumed here.) To bind to a smart card from a managed application, I need to expose several native APIs.

The foundation for just about any smart card application on Windows is SCardEstablishContext. This PC/SC routine, exported from winscard.dll, establishes a handle that allows the application to interact with the smart card resource manager. The function is pretty simple, and for these easy ones I didn't need to implement an additional native wrapper. A P/Invoke interface was sufficient:

[DllImport("winscard.dll")] public static extern Int32 SCardEstablishContext( UInt32 scope, [MarshalAs(UnmanagedType.AsAny)] Object reserved1, [MarshalAs(UnmanagedType.AsAny)] Object reserved2, ref IntPtr pcscContext);

The second PC/SC routine exercised by the sample application, SCardUIDlgSelectCardW, was by far the hardest one to expose in .NET code. There are two reasons for this. First, the function uses a single C-style struct as its parameter. (See OPENCARDNAME_EXW defined in winscard.h in the Platform SDK.) The struct includes, among a number of other fields, two Unicode character arrays that act as both input and output. Marshaling this struct correctly took some trial and error. I should note that this struct has, in turn, an optional embedded struct: OPENCARD_SEARCH_CRITERIAW, also in winscard.h. The nested struct is just as complicated as the first, if not more so. For this sample, I chose not to expose the advanced capabilities of SCardUIDlgSelectCardW that depend on the second struct.

The second challenge in exposing SCardUIDlgSelectCardW is that the first data member of OPENCARDNAME_EXW must be initialized to the size, in bytes, of the struct. I explored three options for initializing that field to the correct value. First, I tried to hardcode it—that is, determine how big the structure is (for example, via a debugger and a simple test application in native code) and always initialize the field to that value in managed code. It's nice to have portable code, though, and this struct will not be the same size in a 64-bit environment.

Second, you can use one or more of the CLR marshaling primitives, such as Marshal.SizeOf, to determine at run time how big the marshaled structure will be. Unfortunately, in my testing, that particular routine threw an exception indicating that the marshaled size of certain data members couldn't be determined. Moreover, unless this approach would produce the exact same value as the native sizeof, it's not a good idea.

Third, I implemented a native wrapper that accepts a partially initialized struct, sets the dwStructSize field, and then calls SCardUIDlgSelectCardW. Then I exposed the native wrapper to managed code via P/Invoke. You've probably already guessed that I chose this option. While I knew that I wanted to package the native wrapper as a separate DLL for flexibility, and adding the management overhead of an additional DLL to support a single API would have been a tough pill to swallow, I already knew that the Card Module routines were going to require a similar solution. In other words, I figured that supplying a separate native DLL was inevitable. I'll cover the card module routines in the next section.

In the sample code, MgSc.dll (an abbreviation for Managed Smart Card) exports the native wrapper routines. The code for the wrapper routines, including MgScSCardUIDlgSelectCardW, is in MgSc.cpp. The P/Invoke class for all of the native wrappers in that DLL is also called MgSc; it contains P/Invoke stubs that look similar to the following:

[DllImport("mgsc.dll", CharSet=CharSet.Unicode)] public static extern Int32 MgScSCardUIDlgSelectCardW( [MarshalAs(UnmanagedType.LPStruct)] [In, Out] PCSCOpenCardNameWEx ocnwex);

After getting this API working, the rest seemed really easy!

GetSmartCard Helper Routine

Figure 3 shows an excerpt of the GetSmartCard helper routine found in the sample code. Notice that, if MgScSCardUIDlgSelectCardW is successful, it implies that the cardHandle member of the PCSCOpenCardNameWEx parameter is now initialized and refers to an inserted smart card. In other words, we're ready to party on that card.

Figure 3 GetSmartCard Helper Routine

static Int32 GetSmartCard( [In, Out] ref MgScContext mgscContext, [In, Out] ref IntPtr pcscContext, [In, Out] ref IntPtr cardHandle) { Int32 result = 0; PCSCOpenCardNameWEx ocnwex = new PCSCOpenCardNameWEx(); bool cardIsLocked = false; string readerName; UInt32 readerNameLength = 0, cardState = 0, cardProtocol = 0; byte [] atr; UInt32 atrLength = 0; try { // Get a context handle from the smart card resource manager if (0 != (result = PCSC.SCardEstablishContext( PCSCScope.SCARD_SCOPE_USER, null, null, ref pcscContext))) throw new Exception("SCardEstablishContext"); // Get a handle to a card, prompting the user if necessary ocnwex.flags = PCSCDialogUI.SC_DLG_MINIMAL_UI; ocnwex.pcscContext = pcscContext; ocnwex.shareMode = PCSCShareMode.SCARD_SHARE_SHARED; ocnwex.preferredProtocols = PCSCProtocol.SCARD_PROTOCOL_Tx; ocnwex.reader = new string( (char) 0, (int) readerAndCardBufferLength); ocnwex.maxReaderLength = readerAndCardBufferLength; ocnwex.card = new string( (char) 0, (int) readerAndCardBufferLength); ocnwex.maxCardLength = readerAndCardBufferLength; if (0 != (result = MgSc.MgScSCardUIDlgSelectCardW(ocnwex))) throw new Exception("SCardUIDlgSelectCardW"); // Lock the card if (0 != (result = PCSC.SCardBeginTransaction(ocnwex.cardHandle))) throw new Exception("SCardBeginTransaction"); cardIsLocked = true; // Get the ATR for the selected card if (0 != (result = PCSC.SCardStatusW(ocnwex.cardHandle, null, ref readerNameLength, ref cardState, ref cardProtocol, null, ref atrLength))) throw new Exception("SCardStatusW"); readerName = new string((char) 0, (int) readerNameLength); atr = new byte [atrLength]; if (0 != (result = PCSC.SCardStatusW(ocnwex.cardHandle, readerName, ref readerNameLength, ref cardState, ref cardProtocol, atr, ref atrLength))) throw new Exception("SCardStatusW"); // Get a card module handle for this card mgscContext = new MgScContext(); if (0 != (result = (int) MgSc.MgScCardAcquireContext( mgscContext, pcscContext, ocnwex.cardHandle, ocnwex.card, atr, atrLength, 0))) throw new Exception("MgScCardAcquireContext"); cardHandle = ocnwex.cardHandle; } finally { if (0 != result) { // Something failed, so we need to cleanup. if (cardIsLocked) PCSC.SCardEndTransaction(ocnwex.cardHandle, PCSCDisposition.SCARD_LEAVE_CARD); if ((IntPtr)0 != ocnwex.cardHandle) PCSC.SCardDisconnect(ocnwex.cardHandle, PCSCDisposition.SCARD_LEAVE_CARD); if ((IntPtr)0 != ocnwex.pcscContext) { PCSC.SCardReleaseContext(ocnwex.pcscContext); pcscContext = (IntPtr)0; } } } return result; }

The first thing I do in GetSmartCard with the returned card handle is call SCardBeginTransaction. This grants exclusive access to the smart card, preventing other applications (or even other card handles within this process) from interacting with the card. If the GetSmartCard routine returns successfully, the transaction is still held, and I'll keep the card locked up until I call the managed Crypto API routines. The reason I release the lock at that time is subtle as I'll explain in a moment.

The second thing I do with the card handle is use it to query PC/SC for the ATR (Answer to Reset) string of the selected smart card. The bytes of the ATR constitute a per-card type identifier that facilitates low-level protocol negotiation between the card and the smart card reader device driver. The card ATR is also used as the mapping, via the system registry, between the card and its corresponding card module (as well as between the card and its corresponding CSP, although those details are abstracted by Crypto API). In other words, in order to load the proper card module, I must first know the ATR of the card I want to talk to.

Since I already have the card handle, I call the SCardStatus API via P/Invoke to get the ATR. You can see in GetSmartCard that I call SCardStatus twice: once to query for the size of its two variable-length output parameters and again with buffers of sufficient size to accept those outputs. But if you take a look at the Platform SDK documentation for SCardStatus, you'll see that the API can optionally allocate the output buffers on behalf of the caller.

Take the ATR output buffer as an example. In native code, the caller can set the buffer-length input/output parameter, pcbAtrLen, to point to the value SCARD_AUTOALLOCATE, which is defined in winscard.h to be -1. The pbAtr parameter is then passed as an LPBYTE *, cast to an LPBYTE. The returned buffer is freed via SCardFreeMemory. The same semantics are available for a number of PC/SC functions. In fact, whenever I write PC/SC-related native code, I make heavy use of SCARD_AUTOALLOCATE for convenience. However, I chose not to expose that capability in the wrapper code due to the additional complexity of defining such chameleon-like behavior and getting it marshaled correctly.

Once I've got the ATR, I'm ready to map the selected card to its card module. I decided to abstract that operation into one of the wrapper functions that I implemented to expose the card module to managed code. That's the topic of the next section.

Card Module API Wrappers

The native card module interface is defined in cardmod.h, which is included in current Windows Vista releases of the Platform SDK. If you take a look at the CARD_DATA struct, defined near the end of the header file, you'll notice that there are a couple of complications in exposing the card module routines in .NET code. Similar to the memory-management-related challenge offered by SCARD_AUTOALLOCATE in some of the PC/SC routines, a few of the card module routines allocate dynamically sized output buffers on behalf of the caller. But, unlike SCARD_AUTOALLOCATE, the card module memory allocation behavior isn't optional. Take, for example, CardReadFile, which I'll need to use in the sample application when I read the digital license file from the smart card. I'll end up calling CardReadFile via a function pointer, the type of which is defined as follows.

typedef DWORD (WINAPI *PFN_CARD_READ_FILE)( __in PCARD_DATA pCardData, __in LPSTR pszDirectoryName, __in LPSTR pszFileName, __in DWORD dwFlags, __out_bcount(*pcbData) PBYTE *ppbData, __out PDWORD pcbData);

Now take a look at two members of the CARD_DATA struct. The first is the heap allocator callback to be used by the card module when creating output buffers. The second is the PFN_CARD_READ_FILE function pointer to be initialized by the card module during CardAcquireContext for subsequent use by the calling application.

// These members must be initialized by the CSP/KSP before // calling CardAcquireContext. ... PFN_CSP_ALLOC pfnCspAlloc; PFN_CSP_FREE pfnCspFree; ... // These members are initialized by the card module ... PFN_CARD_READ_FILE pfnCardReadFile;

In summary, during the CardReadFile call, the card module will do more or less the following: allocate a sufficiently large buffer via pfnCspAlloc, read the requested file from the card via SCardTransmit, set pcbData, and return. Once the caller is finished with the returned data buffer, it calls pfnCspFree.

Rather than crafting a set of P/Invokes, I knew it was going to be quicker to get things working with a native wrapper for each card module routine. For example, in the CardReadFile case, the .NET application calls the native wrapper once to determine the size of the output buffer and then again with a buffer of sufficient size. This is a significant performance tradeoff, since the application is reading the file from the smart card twice. It's likely that the situation could be improved by implementing delegates for the heap callbacks.

Handling CardAcquireContext

Let's return now to the flow of the sample digital license application. Recall that, via PC/SC routines, I've now got a card and its ATR. Based on the ATR, I look up the name of the corresponding card module DLL in what's known as the Calais database, which is the data stored under HKLM\Software\Microsoft\Cryptography\Calais in the system registry. "Calais" was chosen as a project codename when the PC/SC stack was originally being implemented at Microsoft. I like to think it's not totally coincidental that, by some accounts, the smart card was invented by a Frenchman.

The Calais database lookup is accomplished via SCardGetCardTypeProviderName, passing the SCARD_PROVIDER_CARD_MODULE value defined in cardmod.h. Instead of exposing that specific PC/SC routine to the managed world via a P/Invoke interface, I decided instead to roll it into MgScCardAcquireContext, the native code wrapper for CardAcquireContext. Why? Well, let's look forward a few (native code) operations. The next step is to take the DLL string name returned from the Calais database and pass it to LoadLibrary. The resulting HMODULE must be maintained for the lifetime of the context with the card module; I certainly don't want the card module DLL unloaded out from under me. Then I call GetProcAddress to find the CardAcquireContext export. Finally, I initialize the CARD_DATA struct with the native callbacks that the card module will be expecting, including the heap allocation routines.

In summary, there's little benefit in trying to get all of this native-code-related stuff working via .NET code when it's only going to be exercised when preparing to call CardAcquireContext. Better just to roll it into the native wrapper for that routine.

Consider what the card module does under the covers. The steps taken by a given card module in processing CardAcquireContext will depend on the type of card. If the card is based on the ISO command set, there may be no need for the card module to send any commands to the card at this time. Instead, the card module may verify that the specified ATR (input by the caller via the CARD_DATA struct) is indeed supported. It may then set up some internal context state and attach it to the pvVendorSpecific member of the caller's CARD_DATA. On the other hand, newer cards may be proxy- or virtual-machine-based. Sun's Java card is a prominent example. In that case, it's likely that the card module will send a command to the card via SCardTransmit to instantiate a card-side card module API handler applet.

Transaction Management

Let's return now to the sample code. If the GetSmartCard helper routine returns successfully, the transaction on the card is still held. In other words, the card handle that is output from GetSmartCard still has exclusive access to the card. There's more to this than meets the eye. Notice that the overall flow of the sample app is to first encrypt the data via managed Crypto API, then do all of the necessary card module work including writing and reading back the file, and finally decrypt the data via managed Crypto API.

This order of operations highlights a few important considerations of what I call transaction management. First, when calling the PC/SC and card module routines, I want to perform as many operations as needed while minimizing the number of separate transactions. In other words, I'll minimize the number of times I release and re-acquire the exclusive communications link with the smart card. By making all of those calls in a single transaction, I eliminate the possibility of another application intervening and leaving the card in an unknown state.

However, that strategy only goes so far; the second consideration is that Crypto API does its own transaction management. For example, after I read the encrypted file back from the card, I must call SCardEndTransaction before calling managed Crypto API to decrypt the file. Otherwise the call to RSACryptoServiceProvider.Decrypt would block forever on an internal SCardBeginTransaction deep within the CSP.

A third consideration of transaction management, and an important security consideration as well, is that the card must remain locked while authenticated. Via the card module interface, the card becomes authenticated when the correct PIN is transmitted via CardAuthenticatePin. I've exposed this through a P/Invoke interface for MgScCardAuthenticatePin. The PC/SC security model is such that applications that want to perform privileged operations on the card are expected to use the following sequence.

  1. Lock the card via SCardBeginTransaction.
  2. Authenticate to the card. I'm using CardAuthenticatePin to accomplish this.
  3. Perform the privileged operations. In this example, I first attempt to delete a file from the card and then attempt to write a file. Both operations require authentication.
  4. De-authenticate the card.
  5. Unlock the card via SCardEndTransaction.

This way, unauthorized applications are prevented from sending privileged commands to a card that has been authenticated on behalf of an authorized application.

Some cards don't support de-authentication directly. Instead, the application must reset the card via PC/SC. Resetting the card returns it to its initial, unauthenticated state. But resetting effectively "reboots" the smart card, which can adversely affect the performance of the next host application that attempts to communicate with the card. Therefore, the card module interface has been designed with an optional routine to avoid this problem and the resulting performance pitfalls.

In my native wrapper function MgScCardDeauthenticate, if the target card module doesn't implement the CardDeauthenticate function, the wrapper will return ERROR_CALL_NOT_IMPLEMENTED. If the de-authenticate call fails for any reason, the managed application will make a note to itself that the card must be reset before the current transaction is released.

if (0 != (result = (Int32) MgSc.MgScCardDeauthenticate( mgscContext, MgScUserId.User, 0))) cardDispositionOnRelease = PCSCDisposition.SCARD_RESET_CARD;

This is consistent with the transaction management security model that I described earlier.

Card Module Interface Design Rationale

The final details of the interaction between the application and the smart card are the simplest, and have already been mentioned briefly. Namely, to implement the storage-related logic of placing the digital license file on the card, I first call the native MgScCardDeleteFile via P/Invoke. For simplicity I don't check the return value, since if this is the first time executing this application with a given card the file won't exist, in which case the API is expected to return an error.

I next call the wrappers for CreateFile and WriteFile. There's an interesting lesson here on the design rationale for the card module API. Separate CardCreateFile and CardWriteFile routines offer a simple way to replicate the behavior of the CREATE_NEW and OPEN_EXISTING semantics offered by the familiar Windows CreateFile API, including the corresponding error conditions. This is important because it lowers the bar for programmers who now can interact with the card using the same patterns that they've already mastered for file I/O in general.

But unlike the Windows CreateFile function, CardCreateFile takes a byte count parameter as input. Some card modules may ignore that parameter, but it's a good idea for applications to pass in the lowest maximum size of the file to be created. The reason for this is that some cards must preallocate the entire file at creation time. On the other hand, you don't want to waste space. One potentially confusing ramification of this design is that CardReadFile may return the whole allocated size of the file (the potentially larger value passed to CardCreateFile), not just the potentially smaller amount that's been written via CardWriteFile.

After the encrypted license has been written to the card, I read it back via MgScCardReadFile. A production-quality application wouldn't perform this step, but I do it for demonstration purposes and to test the interface. The next step after that is to decrypt the file, again as a sanity check, and compare it to the original value.

Using CLR Crypto

Notice that the first thing I do in the sample code is bind to a smart-card-based RSA key pair via the .NET Crypto API. (See the documentation for class RSACryptoServiceProvider in the System.Security.Cryptography namespace.)

Although the abstractions and power provided by .NET are nice, I'm a firm believer in trying to understand what's happening under the covers. Since the .NET Crypto API is based on the native Crypto API, I can make some assumptions about this code, adapted from my GetKey helper routine:

CspParameters cspParameters = new CspParameters( 1, "Microsoft Base Smart Card Crypto Provider"); cspParameters.Flags = CspProviderFlags.UseExistingKey; cspParameters.KeyContainerName = ""; rsa = new RSACryptoServiceProvider(cspParameters);

I can assume that this code results in, at the minimum, calls to CryptAcquireContext and CryptGetUserKey. If this fails, I set cspParameters.Flags to zero and try again. That will result in a call to CryptAcquireContext followed by CryptGenKey.

Dependencies and Testing

I can make further assertions about what the CSP is probably doing under the covers. This will facilitate the creation of a component dependency diagram for the sample application. The CSP I'm calling is actually built on top of the card module interface; that's the primary rationale for writing a card module in the first place. And the fact that it's a smart card CSP implies that it's probably establishing a connection to the smart card in a similar way that the sample code does before calling directly into the card module routines.

In other words, before I make the explicit calls to SCardUIDlgSelectCardW, SCardBeginTransaction, SCardStatus, and CardAcquireContext that I discussed earlier, the CSP is making more or less the same calls internally. Especially in light of the amount of setup work required just to call CardAcquireContext, doesn't this duplication of effort seem undesirable? It would be nicer if the required context information for calling directly into the card module could be retrieved from Crypto API, or at least exposed via some shared code.

Figure 4 depicts these dependencies. The bottom layer of the diagram refers to everything in the smart card stack from the winscard.dll client API, over to the resource manager service, down to the driver, and out to the smart card.

Figure 4 SampleApp Dependencies

Figure 4** SampleApp Dependencies **

Now that I've discussed some smart card fundamentals as well as the test application in detail, I hope you'll download the sample code and play around with it. To build and run the sample code requires a few specific items:

  • The cardmod.h header from a recent version of the Windows Vista Platform SDK.
  • A card-module-based smart card. That is, a smart card for which the vendor has supplied a card module.
  • A smart card reader. In some cases, such as certain USB keys, the smart card and reader is a single unit.
  • The Base Smart Card Crypto Provider. This can be downloaded via Windows Update. Click the Custom button then select Software, Optional on the left-hand side.

At the time of this article's publication, the latest Platform SDK release is Beta 2; it can be downloaded from www.microsoft.com/downloads. You can drop the headers into your existing Visual Studio® build environment or into an older version of the Platform SDK environment. I did the former and it worked fine.

I'll post updates to my blog at www.jwsecure.com. Reader comments and questions are welcome there as well. Have fun!

Dan Griffin is a software security consultant in Seattle, WA. He previously spent seven years at Microsoft on the Windows Security development team. Dan can be reached via www.jwsecure.com.