Skip to content

Instantly share code, notes, and snippets.

@trueroad
Last active March 22, 2024 12:26
Show Gist options
  • Save trueroad/ad9c222a667d5091a118196301b938a0 to your computer and use it in GitHub Desktop.
Save trueroad/ad9c222a667d5091a118196301b938a0 to your computer and use it in GitHub Desktop.
PC/SC test
//
// PC/SC test
// https://gist.github.com/trueroad/ad9c222a667d5091a118196301b938a0
//
// Copyright (C) 2024 Masamichi Hosoda.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Compile:
// x86_64-w64-mingw32-gcc -o pcsc-test pcsc-test.c -lwinscard
// References:
// https://qiita.com/t13801206/items/2dbadfcd752a3e5391a4
// https://eternalwindows.jp/security/scard/scard00.html
#define STRICT
#include <stdio.h>
#include <windows.h>
#include <winscard.h>
#define APDU_CLA_GENERIC 0xff
#define APDU_INS_GET_DATA 0xca
#define APDU_P1_GET_UID 0x00
#define APDU_P2_NONE 0x00
#define APDU_LE_MAX_LENGTH 0x00
SCARDCONTEXT ghContext = 0;
LPTSTR glpszReaderName = NULL;
SCARDHANDLE ghCard = 0;
void close_all (void)
{
if (ghCard)
{
SCardDisconnect (ghCard, SCARD_LEAVE_CARD);
ghCard = 0;
}
if (glpszReaderName)
{
SCardFreeMemory (ghContext, glpszReaderName);
glpszReaderName = NULL;
}
if (ghContext)
{
SCardReleaseContext (ghContext);
ghContext = 0;
}
fprintf (stderr, "closed all\n");
}
BOOL WINAPI CtrlHandler (DWORD fdwCtrlType)
{
fprintf (stderr, "CtrlHandler\n");
close_all ();
return FALSE;
}
int main ()
{
fprintf (stderr,
"PC/SC test\n"
"Copyright (C) 2024 Masamichi Hosoda\n"
"https://gist.github.com/trueroad/ad9c222a667d5091a118196301b938a0"
"\n\n");
if (!SetConsoleCtrlHandler (CtrlHandler, TRUE))
{
fprintf (stderr, "Error: SetConsoleCtrlHandler\n");
return 1;
}
LONG res = SCardEstablishContext (SCARD_SCOPE_USER,
NULL,
NULL,
&ghContext);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr, "Failed: SCardEstablishContext: 0x%lx\n", res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardEstablishContext\n");
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
res = SCardListReaders (ghContext,
SCARD_ALL_READERS,
(LPTSTR)&glpszReaderName,
&dwAutoAllocate);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr, "Failed: SCardListReaders: 0x%lx\n", res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardListReaders: %s\n", glpszReaderName);
SCARD_READERSTATE readerState;
readerState.szReader = glpszReaderName;
readerState.dwCurrentState = SCARD_STATE_UNAWARE;
res = SCardGetStatusChange (ghContext, 0, &readerState, 1);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr, "Failed: SCardGetStatusChange: 0x%lx\n", res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardGetStatusChange\n");
while (TRUE)
{
while (!(readerState.dwEventState & SCARD_STATE_PRESENT))
{
fprintf (stderr, "Waiting for a card to be set...\n");
readerState.dwCurrentState = readerState.dwEventState;
res = SCardGetStatusChange (ghContext,
INFINITE,
&readerState,
1);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr,
"Failed: SCardGetStatusChange: 0x%lx\n",
res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardGetStatusChange: 0x%ld\n",
readerState.dwEventState);
}
printf ("SCARD_STATE_PRESENT\n");
fprintf (stderr, "ATR: %lu bytes\n", readerState.cbAtr);
for (unsigned int u = 0; u < readerState.cbAtr; u++)
{
fprintf (stderr, " %02x", readerState.rgbAtr[u]);
}
fprintf (stderr, "\n");
DWORD dwActiveProtocol;
res = SCardConnect (ghContext,
glpszReaderName,
SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
&ghCard,
&dwActiveProtocol);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr, "Failed: SCardConnect: 0x%lx\n", res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardConnect\n");
BYTE command[] =
{APDU_CLA_GENERIC, APDU_INS_GET_DATA,
APDU_P1_GET_UID, APDU_P2_NONE,
APDU_LE_MAX_LENGTH};
fprintf (stderr, "Transmitting: %u bytes\n", sizeof(command));
for (unsigned int u = 0; u < sizeof(command); u++)
{
fprintf (stderr, " %02x", command[u]);
}
fprintf (stderr, "\n");
BYTE response[256];
DWORD dwResponseSize = sizeof(response);
res = SCardTransmit (ghCard,
SCARD_PCI_T1,
command,
sizeof(command),
NULL,
response,
&dwResponseSize);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr, "Failed: SCardTransmit: 0x%lx\n", res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardTransmit\n");
SCardDisconnect (ghCard, SCARD_LEAVE_CARD);
ghCard = 0;
fprintf (stderr, "Response: %lu bytes\n", dwResponseSize);
for (unsigned int u = 0; u < dwResponseSize; u++)
{
fprintf (stderr, " %02x", response[u]);
}
fprintf (stderr, "\n");
BYTE sw1 = response[dwResponseSize - 2];
BYTE sw2 = response[dwResponseSize - 1];
if (sw1 != 0x90 || sw2 != 0x00)
{
fprintf (stderr, "Card response error: sw1 = 0x%02x, sw2 = 0x%02x\n",
sw1, sw2);
close_all ();
return 1;
}
for (unsigned int u = 0; u < (dwResponseSize - 2); u++)
{
printf ("%02x", response[u]);
}
printf ("\n");
while (!(readerState.dwEventState & SCARD_STATE_EMPTY))
{
fprintf (stderr, "Waiting for a card to be unset...\n");
readerState.dwCurrentState = readerState.dwEventState;
res = SCardGetStatusChange (ghContext,
INFINITE,
&readerState,
1);
if (res != SCARD_S_SUCCESS)
{
fprintf (stderr,
"Failed: SCardGetStatusChange: 0x%lx\n",
res);
close_all ();
return 1;
}
fprintf (stderr, "Succeeded: SCardGetStatusChange: 0x%ld\n",
readerState.dwEventState);
}
printf ("SCARD_STATE_EMPTY\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment