Tuesday, April 10, 2012

smartcard_name_and_provider implementation

Once the reader monitor has reported the ATR for the card in a reader, it can be used to access the card.

Windows has a model that gives card manufacturers three options for providing access to a smart card through the crypto apis.

  • KSP/CSP - the manufacturer can supply a full KSP(Key Storage Provider) and CSP (Cryptographic Service Provider) and associate it with an ATR.
  • Smart Card Minidriver - the manufacturer can supply a minidriver dll that the built-in Smart Card KSP and CSP will load to provide access and associate it with an ATR.
  • PICS/GIDS - the manufacturer can provide a card that implements the GIDS or PICS card standard. Windows has built-in minidrivers for these.

Because of the first option, once an ATR is in hand, SCardGetCardTypeProviderName must be called to retrieve the KSP or CSP provider name that should be used for the card.

SCardListCards will retrieve the card name from an ATR, which is then passed to SCardGetCardTypeProviderName. Windows only supports one KSP/CSP for an ATR, so even though SCardListCards returns a list of card names any one of them can be used to retrieve the provider names.

Here is the code to get a card name and the KSP provider name.

struct CardWithProvider {
  std::wstring cardname;
  std::wstring kspname;
};

inline
std::pair<unique_winerror, CardWithProvider>
smartcard_name_and_provider(
  lib::rng::range<const BYTE*> atr
)
{
  unique_winerror winerror;
  unique_close_scardcontext context;

  CardWithProvider output;

  winerror.reset(
    SCardEstablishContext(
        SCARD_SCOPE_USER,
        NULL,
        NULL,
        context.replace()
    )
  );
  if (!winerror) {
    return std::make_pair(winerror, std::move(output));
  }

  LPWSTR kspstring = nullptr;
  ON_UNWIND(
    unwind_kspstring,
    [&] { 
      if (kspstring) {
        SCardFreeMemory(context.get(), kspstring);
      }
    }
  );
  DWORD ksplength = SCARD_AUTOALLOCATE;

  LPWSTR cardsstring = nullptr;
  ON_UNWIND_AUTO(
    [&] { 
      if (cardsstring) {
        SCardFreeMemory(context.get(), cardsstring);
      }
    }
  );
  DWORD cardslength = SCARD_AUTOALLOCATE;

  winerror.reset(
    SCardListCards(
        context.get(),
        atr.begin(),
        NULL,
        NULL,
        reinterpret_cast<LPWSTR>(&cardsstring),
        &cardslength
    )
  );
  if (winerror == 
      winerror_cast(SCARD_E_READER_UNAVAILABLE)) {
    winerror.suppress();
    // don't free the static string we are 
    // about to assign
    unwind_kspstring.dismiss();
    kspstring = MS_SMART_CARD_KEY_STORAGE_PROVIDER;
    ksplength = wcslen(kspstring);
  } else if (!winerror) {
    return std::make_pair(winerror, std::move(output));
  } else {
    winerror.reset(
      SCardGetCardTypeProviderName(
          context.get(),
          cardsstring,
          SCARD_PROVIDER_KSP,
          reinterpret_cast<LPWSTR>(&kspstring),
          &ksplength
      )
    );
    if (!winerror) {
      return std::make_pair(winerror, std::move(output));
    }
  }

  if (cardsstring) {
    output.cardname = cardsstring;
  }
  output.kspname = kspstring;

  return std::make_pair(winerror, std::move(output));
}

No comments:

Post a Comment