Developer Guidelines

This topic discusses general guidelines for developers who work with, and/or develop smart card minidrivers - to let you know the expected behavior of the smart card and its associated applications.

General Design Guidelines

For information about how to distribute card minidrivers, how to map logical card file-systems and other minidriver design guidelines, see the General Design Guidelines section under Smart Card Minidriver Overview.

Challenge/Response Method of Unblocking Smart Card PIN

For an administrator to successfully use this mechanism to unblock a user’s card, administrators must be able to identify and use the administrator key that is stored on the card so that they can correctly generate the response data to the challenge that was issued.

One way to do this is to use the card identifier to uniquely identify the card. (The card identifier is a unique identifier for a card.) This can be represented in some form to users in the UI, but otherwise a program could be written to send appropriate APDU commands to the card to read this information.

This information can then allow the administrator to identify the secret key on the card and calculate the appropriate response to the challenge data that is issued to users.

It is assumed that the administrator secret key stored on a card is held by using some secure mechanism that is accessible only to valid and trusted administrators (preferably as few as possible). However, this is beyond the scope of this specification.

For more information, see the following “Challenge/Response Mechanism” section.

Enhanced PIN Support

Version 6.0 supported a flexible architecture for multiple PIN support. This architecture introduced a new concept of roles in which each role corresponds to a PIN identifier. The PIN identifiers are used to extract PIN information from the card, as well as to associate a PIN with a key container.

The identifier consists of a number, currently limited to 0 through7. We also introduced the notion of a PIN_SET, which is a bitmask that can be generated from the PIN identifier. Currently only the lower 8 bits are used for the PIN set. We can also choose to use the remaining bits to indicate conditions such as ‘and’, ‘or’, or other information that we might find useful in the future. We chose this approach so that the bit mask is easy for the card to enforce.

Assume that the user authenticates with role 3, corresponding to PIN #3. This translates to the bit mask 0000 0100 (base 2). The card can record this as the currently authenticated ID and can easily verify access control rules on keys and PINs by doing a bit-wise AND operation. The design allows having multiple authenticated identities on the card simultaneously, and this is a requirement for cards that support v6 card minidrivers. As an example, if PIN #1 is authenticated and then subsequently PIN #2 is authenticated, operations that any of these PINs control should be allowed.

Session PINs and Secure PIN Channel

When Windows must establish a secure PIN channel for PIN authentication, the following sequence of operations is performed with the minidriver. To comply, a minidriver and the card must be compatible with the following sequence. In particular, session PINs should be transferable between processes and last for only a certain length of time. (We recommend that any session PIN be valid until the cold reset of the card by using the CARD_AUTHENTICATE_ SESSION_PIN flag even if CardAuthenticateEx is called with the GENERATE_SESSION_PIN flag set.)

The following behavior should be supported:

  1. Application A, a trusted system process, acquires a handle to the smart card and collects a PIN.
  2. Application A then calls the card CardAuthenticateEx minidriver function, and passes the PIN that was collected and sets the CARD_AUTHENTICATE_GENERATE_SESSION_PIN flag. This does not cause the card to be unlocked.
  3. Application A stores the session PIN that was generated and releases the handle to the card and card minidriver. The card is not cold reset.
  4. Application A sends the session PIN and the name of the reader that has the card that was acquired in step 1 to Application B.
  5. Application B acquires the same card as in 1.
  6. Application B calls CardAuthenticateEx and passes in the session PIN and sets the CARD_AUTHENTICATE_SESSION_PIN flag. If the session PIN is still valid, the card should be authenticated and valid for use.
  7. When Application B is finished using the card, it calls CardDeauthenticateEx to deauthorize the card.

This behavior has the following practical limitations:

  • Cards must declare their ability to work with session PINs by returning the appropriate value for CP_CARD_PIN_STRENGTH_VERIFY.

  • Cards that rely on having the PIN for each verification are not compatible with this system.

  • Several applications can have what they determine to be valid session PINs at any one time. If only one session PIN is possible for each PIN, the following implementation is advised:

    • The card should remember the most recent session PIN that was generated.
    • If an invalid session PIN is presented, the card should fail the authentication and, if supported, decrement the retry counter for the session PIN. If the retry count reaches 0 and the next authentication attempt is invalid, the session PIN should be invalidated.
    • Subsequent session PIN presentations should fail until a new session PIN is negotiated.
  • The session PIN must be able to be used from different applications on the system.

  • The session PIN must not simply be an encoding of the PIN.

  • The security of this system is limited to the strength of the session PIN and the negotiation protocol that is used to generate it. The actual session PIN negotiation is outside the scope of this specification. We make no requirements on the design except that it works as described in this section.

  • The session PIN is still considered valuable and should be treated as a secret.

  • The card should be able to detect an invalid session PIN.

Read-Only Cards

To address cards that are personalized outside the Base CSP/KSP environment and are inherently read-only, we have introduced a new concept of read-only cards. If a card is read-only, it must advertise this through the CardGetProperty function (see this section earlier in this specification).

Read-only cards must support only a subset of the Version 7 card minidriver interface and are not required to support an administrator PIN.

The following table lists the functions that a read-only card must support.

Function name Required Notes
CardAcquireContext Yes
CardDeleteContext Yes
CardAuthenticatePin Yes
CardGetChallenge No (Optional)
CardAuthenticateChallenge No (Optional)
CardDeauthenticate Yes (Optional)
CardUnblockPin No (Optional)
CardChangeAuthenticator No (Optional)
CardCreateDirectory No
CardDeleteDirectory No
CardReadFile Yes Card minidriver must emulate a file system.
CardCreateFile No
CardGetFileInfo Yes Card minidriver must emulate a file system.
CardWriteFile No
CardDeleteFile No
CardEnumFiles Yes Card minidriver must emulate a file system.
CardQueryFreeSpace Yes Card minidriver must emulate a file system.
CardQueryCapabilities Yes Card minidriver must emulate a file system.
CardCreateContainer No
CardCreateContainerEx No (Optional)
CardDeleteContainer No
CardGetContainerInfo Yes
CardRSADecrypt Yes (Optional)
CardConstructDHAgreement Yes (Optional)
CardDeriveKey Yes (Optional)
CardDestroyDHAgreement Yes (Optional)
CardSignData Yes
CardQueryKeySizes Yes
CardAuthenticateEx Yes
CardChangeAuthenticatorEx No (Optional)
CardDeauthenticateEx Yes
CardGetChallengeEx No (Optional)
CardGetContainerProperty Yes
CardSetContainerProperty No
CardGetProperty Yes
CardSetProperty Yes
MDImportSessionKey No (Optional)
MDEncryptData No (Optional)
CardImportSessionKey No (Optional)
CardGetSharedKeyHandle No (Optional)
CardGetAlgorithmProperty No (Optional)
CardGetKeyProperty No (Optional)
CardSetKeyProperty No (Optional)
CardDestroyKey No (Optional)
CardProcessEncryptedData No (Optional)
Legend
Yes This function must be implemented.
No Entry point must exist and must return SCARD_E_UNSUPPORTED_FEATURE.
No (Optional) The operation is not required to be supported for a read-only card, but may be implemented if the card supports the operation. If not supported, the entry point must return SCARD_E_UNSUPPORTED_FEATURE.
Yes (Optional) This function should be implemented according to its definition in this specification, regardless of whether the card is read-only.

The following requirements should be considered when developing a minidriver for a read-only card:

  • All expected Base CSP/KSP files, with the exception of the ‘msroots’ file (such as ‘cardcf’ and ‘cardid’) must exist on the read-only card (or must be virtualized through the minidriver interface).
  • A read-only card must contain at least one key on the card that is protected by the primary card (that is, ROLE_USER) PIN.
  • A read-only card is allowed to not contain an admin key. If this is the situation, it is expected that the minidriver will not support CardGetChallenge, CardAuthenticateChallenge, and CardUnblockPin.
  • When queried, a read-only card should return 0 bytes available and 0 containers available.
  • Only the CP_PARENT_WINDOW and CP_PIN_CONTEXT_STRING properties should be allowed to be set on a read-only card.
  • For a read-only card, the CP_SUPPORTS_WIN_X509_ENROLLMENT property should be false.

Cache Modes

The Base CSP/KSP supports three different modes of caching depending on the cache mode that was returned by the CardGetProperty called with the parameter CP_CARD_CACHE_MODE:

  • If the returned flag is CP_CACHE_MODE_GLOBAL_CACHE and the card reported the CP_READ_ONLY_CARD property as TRUE, the Base CSP/KSP data cache is a global cache. If the card is read-only, the Base CSP/KSP does not write to the cardcf file. If the card can be written to the Base CSP/KSP, it will operate as today.
  • For more information about CP_CARD_CACHE_MODE and CP_CACHE_MODE_GLOBAL_CACHE, see CardGetProperty.
  • When the returned flag is CP_CACHE_MODE_SESSION_ONLY, the Base CSP/KSP operates so that the data cache is cleared when it detects that the card has been removed or reinserted. In other words, we have defined a session to be the span between card insertion and removal.
  • The cache is also implemented for each process and is not global. This mode is designed for read-only cards that do not change on a user’s PC, but rather at some government station or other external site. (This mode is supported for read/write cards, but we recommend the global cache for these cards.)
  • If the card is read-only and there is a chance that the card will change on the user’s PC (by means other than Base CSP/KSP), the application should use the no cache mode that is described later in this specification to avoid the situation in which the cache could contain stale data.
  • When the flag is CP_CACHE_MODE_NO_CACHE, the Base CSP/KSP does not implement any data caching. This mode is designed for card minidrivers that do not support writing the cardcf file, but where the card state can change. The card minidriver decides whether it wants to do any caching in its layer.

Challenge/Response Mechanism

The card minidriver interface supports a challenge/response authentication mechanism. The card must generate a challenge of one or more 8 byte blocks. The authenticating entity calculates the response by encrypting the challenge by using Triple DES (3DES) that operates operating in CBC mode with a 168-bit key (and ignoring the parity bits).

The card verifies the response by using one of the following methods:

  • Repeating the encryption operation on the previously issued challenge and comparing the results.
  • Decrypting the response and comparing the result to the challenge.

If the resulting values are the same, the authentication is successful.

Both the card and the authenticating entity must use the same symmetric key.

The following sample code details how the authenticating entity could calculate the response. This code does not cover any associated warranties and is provided merely as an example and guidance.

#include <windows.h>
#include <wincrypt.h>
#include <winscard.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>


int __cdecl wmain(int argc, __in_ecount(argc) WCHAR **wargv)
{
    //Acquire the context Use CryptAcquireContext

    HCRYPTPROV hProv= 0;
    DWORD dwMode=CRYPT_MODE_ECB;
    BYTE *pbLocData = NULL,tempbyte;
    DWORD cbLocData = 8, count = 0;
    HCRYPTKEY hKey = 0;
    BYTE rgEncByte [] = {0xA8,0x92,0xD7,0x56,0x01,0x61,0x7C,0x5D };

    BYTE DesKeyBlob [] = {
        0x08, 0x02, 0x00, 0x00, 0x03, 0x66, 0x00, 0x00,
        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00
    };

    pbLocData = (BYTE *) malloc (sizeof(BYTE)*cbLocData);
    memcpy(pbLocData,rgEncByte,cbLocData);

    if (!CryptAcquireContext(
            &hProv,
            NULL,
            L"Microsoft Enhanced Cryptographic Provider V1.0",
            PROV_RSA_FULL,
            CRYPT_VERIFYCONTEXT))
    {
        printf("Acquire context failed with 0x%08x \n", GetLastError());
        goto Cleanup;
    }
    if (!CryptImportKey(
            hProv,
            DesKeyBlob,
            sizeof(DesKeyBlob),
            0,
            0,
            &hKey ) )
    {
        printf("Error 0x%08x in importing the 3Des key \n", GetLastError());
        goto Cleanup;
    }
    if (!CryptSetKeyParam(
            hKey,
            KP_MODE,
            (BYTE *)&dwMode,
            0))
    {
        printf("Error 0x%08x in CryptSetKeyParam \n", GetLastError());
        goto Cleanup;
    }
    if (!CryptEncrypt(
            hKey,
            0,
            FALSE,
            0,
            pbLocData,
            &cbLocData,
            cbLocData))
    {
        printf("Error 0x%08x in CryptEncrypt call \n", GetLastError());
        goto Cleanup;
    }

    for (count=0; count < cbLocData; ++count)
    {
        printf("0x%02x",pbLocData[count]);
    }
    printf("\n");

Cleanup:    
    if (hKey)
    {
        CryptDestroyKey(hKey);
        hKey = 0;
    }
    if (pbLocData)
    {
        free(pbLocData);
        pbLocData = NULL;
    }
    if (hProv)
    {
        CryptReleaseContext(hProv,0);
    }

    return 0;
}

Interoperability with msroots

The msroots file is a PKCS #7 formatted certificate store for enterprise trusted roots. (The file is a bag of certificates with empty content and an empty signature and is written and read by the Base CSP.) Card minidriver developers are not required to write any special code in the card minidriver to handle this file. When storing certificates in msroots file, properties such as CODE_SIGNING EKU are not propagated to the smart card because the msroots file stores certificates in a format different from the machine stores. Developers who want to read or write this file from other applications can use the following sample code snippets to access the data.

Read operations

if (FALSE == CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                                &dbStore,
                                CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
                                CERT_QUERY_FORMAT_FLAG_BINARY,
                                0,
                                NULL,
                                NULL,
                                NULL,
                                phCertStore,
                                NULL,
                                NULL))
{
    dwSts = GetLastError();
}

Write operations

// Serialize the store

if (FALSE == CertSaveStore( hCertStore,
                            PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                            CERT_STORE_SAVE_AS_PKCS7,
                            CERT_STORE_SAVE_TO_MEMORY,
                            &dbStore,
                            0))
{
    dwSts = GetLastError();
    goto Ret;
}

dbStore.pbData = CspAllocH(dbStore.cbData);

if (NULL == dbStore.pbData)
{
    dwSts = ERROR_NOT_ENOUGH_MEMORY;
    goto Ret;
}

if (FALSE == CertSaveStore( hCertStore,
                            PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                            CERT_STORE_SAVE_AS_PKCS7,
                            CERT_STORE_SAVE_TO_MEMORY,
                            &dbStore,
                            0))
{
    dwSts = GetLastError();
    goto Ret;
}

Group Policy Settings for Microsoft Base Smart Card CSP

Group Policy settings for the Microsoft Base Smart Card Crypto Service Provider are located in [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults \Provider\Microsoft Base Smart Card Crypto Provider].

Key Description
DefaultPrivateKeyLenBits

dword:00000400

Default key generation parameter—1024-bit key.

RequireOnCardPrivateKeyGen

dword:00000000

This sets the flag for requiring on-card private key generation (default). If this value is set, the key that is generated on a host can be imported into the card. This is used for cards that do not support on-card key generation or where key escrow is required.

TransactionTimeoutMilliseconds

dword:000005dc

1500, 1.5 seconds is the default time-out for holding transactions to the card.

AllowPrivateSignatureKeyImport

dword:00000000

Allows importing signature keys, that is, key archival scenarios.

AllowPrivateExchangeKeyImport

dword:00000000

Allows importing exchange keys, that is, key archival scenarios.

Group Policy Settings for Microsoft CNG Smart Card KSP

Group Policy Settings for Microsoft CNG Smart Card Key Storage Provider are located in [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography \Providers\Microsoft Smart Card Key Storage Provider].

Key Description
DefaultPrivateKeyLenBits

dword:00000400

Default key generation parameter—1024-bit key.

RequireOnCardPrivateKeyGen

dword:00000000

This sets the flag for requiring on-card private key generation (default). If this value is set, the key that is generated on a host can be imported into the card. This is used for cards that do not support on-card key generation or where key escrow is required.

TransactionTimeoutMilliseconds

dword:000005dc

1500, 1.5 seconds is the default time-out for holding transactions to the card.

AllowPrivateSignatureKeyImport

dword:00000000

Allows importing signature keys, that is, key archival scenarios.

AllowPrivateExchangeKeyImport

dword:00000000

Allows importing exchange keys, that is, key archival scenarios.

AllocPrivateECDHEKeyImport

dword:00000000

Allows importing ECDH keys, that is, key archival scenarios

AllowPrivateECDSAKeyImport

dword:00000000

Allows importing ECDSA keys, that is, key archival scenarios

Special Considerations

  • In Windows Vista with Service Pack 1 (SP1), while the operating system is running in safe mode, no PIN-required smart card operations are possible, other than Windows logon.

  • Calling CryptAcquireContext with one of the following flags prompts for PIN authentication with USER_PIN regardless of the actual PIN that is assigned to the container:

    • CRYPT_NEWKEYSET
    • CRYPT_DEFAULT_CONTAINER_OPTIONAL
    • CRYPT_DELETEKEYSET
    • CRYPT_VERIFYCONTEXT
  • CardDeleteContext can be called even after DllMain was called with DLL_PROCESS_DETACH.