a9104f7e |
1 | //----------------------------------------------------------------------------- |
2 | // Copyright (C) 2019 piwi |
3 | // |
4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, |
5 | // at your option, any later version. See the LICENSE.txt file for the text of |
6 | // the license. |
7 | //----------------------------------------------------------------------------- |
8 | // PCSC functions to use alternative Smartcard Readers |
9 | //----------------------------------------------------------------------------- |
10 | |
11 | #include "pcsc.h" |
12 | |
13 | #include <stdlib.h> |
14 | #include <stdbool.h> |
15 | #include <stdio.h> |
16 | #include <string.h> |
17 | |
18 | #if defined (__APPLE__) |
19 | #include <PCSC/winscard.h> |
20 | #include <PCSC/wintypes.h> |
21 | #define SCARD_ATTR_VALUE(Class, Tag) ((((ULONG)(Class)) << 16) | ((ULONG)(Tag))) |
41bdfce3 |
22 | #define SCARD_CLASS_ICC_STATE 9 |
a9104f7e |
23 | #define SCARD_ATTR_ATR_STRING SCARD_ATTR_VALUE(SCARD_CLASS_ICC_STATE, 0x0303) |
24 | #elif defined (_WIN32) |
25 | #include <winscard.h> |
26 | #else |
27 | #include <winscard.h> |
28 | #include <reader.h> |
29 | #endif |
30 | |
31 | #include "ui.h" |
32 | #include "util.h" |
33 | #include "cmdhw.h" |
34 | |
41bdfce3 |
35 | #define PM3_SMARTCARD_DEFAULT_NAME "PM3 RDV40 Smartcard Slot" |
36 | |
a9104f7e |
37 | static SCARDCONTEXT SC_Context; |
38 | static SCARDHANDLE SC_Card; |
39 | static DWORD SC_Protocol; |
40 | static char* AlternativeSmartcardReader = NULL; |
41 | |
53fb848a |
42 | #define PCSC_MAX_TRACELEN 60000 |
43 | static uint8_t pcsc_trace_buf[PCSC_MAX_TRACELEN]; |
44 | static bool tracing = false; |
45 | static uint32_t traceLen = 0; |
46 | |
47 | |
48 | uint8_t *pcsc_get_trace_addr(void) |
49 | { |
50 | return pcsc_trace_buf; |
51 | } |
52 | |
53 | |
54 | uint32_t pcsc_get_traceLen(void) |
55 | { |
56 | return traceLen; |
57 | } |
58 | |
59 | |
60 | static void pcsc_clear_trace(void) |
61 | { |
62 | traceLen = 0; |
63 | } |
64 | |
65 | |
66 | static void pcsc_set_tracing(bool enable) { |
67 | tracing = enable; |
68 | } |
69 | |
70 | |
71 | static bool pcsc_LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, bool readerToTag) |
72 | { |
73 | if (!tracing) return false; |
74 | |
75 | uint8_t *trace = pcsc_trace_buf; |
76 | |
77 | uint32_t num_paritybytes = (iLen-1)/8 + 1; // number of paritybytes |
78 | uint32_t duration = timestamp_end - timestamp_start; |
79 | |
80 | // Return when trace is full |
81 | if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= PCSC_MAX_TRACELEN) { |
82 | tracing = false; // don't trace any more |
83 | return false; |
84 | } |
85 | // Traceformat: |
86 | // 32 bits timestamp (little endian) |
87 | // 16 bits duration (little endian) |
88 | // 16 bits data length (little endian, Highest Bit used as readerToTag flag) |
89 | // y Bytes data |
90 | // x Bytes parity (one byte per 8 bytes data) |
91 | |
92 | // timestamp (start) |
93 | trace[traceLen++] = ((timestamp_start >> 0) & 0xff); |
94 | trace[traceLen++] = ((timestamp_start >> 8) & 0xff); |
95 | trace[traceLen++] = ((timestamp_start >> 16) & 0xff); |
96 | trace[traceLen++] = ((timestamp_start >> 24) & 0xff); |
97 | |
98 | // duration |
99 | trace[traceLen++] = ((duration >> 0) & 0xff); |
100 | trace[traceLen++] = ((duration >> 8) & 0xff); |
101 | |
102 | // data length |
103 | trace[traceLen++] = ((iLen >> 0) & 0xff); |
104 | trace[traceLen++] = ((iLen >> 8) & 0xff); |
105 | |
106 | // readerToTag flag |
107 | if (!readerToTag) { |
108 | trace[traceLen - 1] |= 0x80; |
109 | } |
110 | |
111 | // data bytes |
112 | if (btBytes != NULL && iLen != 0) { |
113 | for (int i = 0; i < iLen; i++) { |
114 | trace[traceLen++] = *btBytes++; |
115 | } |
116 | } |
117 | |
118 | // dummy parity bytes |
119 | if (num_paritybytes != 0) { |
120 | for (int i = 0; i < num_paritybytes; i++) { |
121 | trace[traceLen++] = 0x00; |
122 | } |
123 | } |
124 | |
125 | return true; |
126 | } |
127 | |
a9104f7e |
128 | |
129 | char *getAlternativeSmartcardReader(void) |
130 | { |
41bdfce3 |
131 | return AlternativeSmartcardReader ? AlternativeSmartcardReader : PM3_SMARTCARD_DEFAULT_NAME; |
a9104f7e |
132 | } |
133 | |
134 | |
135 | bool pcscCheckForCardReaders(void) |
136 | { |
137 | LONG res = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &SC_Context); |
138 | if (res != SCARD_S_SUCCESS) { |
139 | return false; |
140 | } |
141 | |
142 | DWORD pcchReaders; |
143 | res = SCardListReaders(SC_Context, NULL, NULL, &pcchReaders); |
144 | if (res != SCARD_S_SUCCESS) { |
145 | SCardReleaseContext(SC_Context); |
146 | return false; |
147 | } |
148 | |
149 | if (res == SCARD_E_NO_READERS_AVAILABLE || res == SCARD_E_NO_SERVICE) { |
150 | SCardReleaseContext(SC_Context); |
151 | return false; |
152 | } |
41bdfce3 |
153 | |
a9104f7e |
154 | return true; |
155 | } |
156 | |
157 | |
158 | static char *pickReader(LPTSTR readerlist) |
159 | { |
160 | PrintAndLogEx(NORMAL, "Please select one of these:"); |
41bdfce3 |
161 | PrintAndLogEx(NORMAL, " [0] %s %s", PM3_SMARTCARD_DEFAULT_NAME, PM3hasSmartcardSlot() ? "(default)" : "(default, not available)"); |
a9104f7e |
162 | |
163 | int num = 1; |
164 | for (LPTSTR p = readerlist; *p != '\0'; ) { |
165 | PrintAndLogEx(NORMAL, " [%1d] %s", num++, p); |
166 | while (*p++ != '\0') ; // advance to next entry |
167 | } |
168 | |
169 | num--; |
41bdfce3 |
170 | |
a9104f7e |
171 | if (num == 1) { |
172 | printf("Your choice (0 or 1)?"); |
173 | } else { |
174 | printf("Your choice (0...%d)? ", num); |
175 | } |
176 | int selection = getch() - '0'; |
41bdfce3 |
177 | printf("\n"); |
a9104f7e |
178 | |
179 | if (selection == 0) { |
41bdfce3 |
180 | PrintAndLogEx(INFO, "Selected %s", PM3_SMARTCARD_DEFAULT_NAME); |
a9104f7e |
181 | return NULL; |
182 | } |
183 | |
184 | if (selection >= 1 && selection <= num) { |
185 | LPTSTR p = readerlist; |
186 | for (int i = 1; i < selection; i++) { |
187 | while (*p++ != '\0') ; // advance to next entry |
188 | } |
189 | PrintAndLogEx(INFO, "Selected %s", p); |
190 | return p; |
191 | } |
192 | |
41bdfce3 |
193 | PrintAndLogEx(INFO, "Invalid selection. Using %s", PM3_SMARTCARD_DEFAULT_NAME); |
a9104f7e |
194 | return NULL; |
41bdfce3 |
195 | |
196 | } |
197 | |
198 | |
199 | static bool matchString(char *string, const char *search) |
200 | { |
201 | if (search[0] == '*' && search[1] == '\0') { // the wildcard only string "*" matches everything |
202 | return true; |
203 | } |
204 | |
205 | if (search[0] == '\0' && string[0] != '\0') { // string is longer than pattern. No match. |
206 | return false; |
207 | } |
208 | |
209 | if (search[0] == '?' || search[0] == string[0]) { // wildcard '?' matches any character |
210 | return matchString(string + 1, search + 1); |
211 | } |
212 | |
213 | if (search[0] == '*') { // wildcard '*' matches any sequence of characters |
214 | for (size_t i = 0; i < strlen(string); i++) { |
215 | if (matchString(string + i, search + 1)) { |
216 | return true; |
217 | } |
218 | } |
219 | } |
220 | |
221 | return false; |
a9104f7e |
222 | } |
223 | |
224 | |
41bdfce3 |
225 | static char *matchReader(LPTSTR readerlist, const char *readername) |
a9104f7e |
226 | { |
41bdfce3 |
227 | if (matchString(PM3_SMARTCARD_DEFAULT_NAME, readername)) { |
228 | PrintAndLogEx(INFO, "Selected %s", PM3_SMARTCARD_DEFAULT_NAME); |
229 | return NULL; |
230 | } |
231 | |
232 | for (LPTSTR p = readerlist; *p != '\0'; ) { |
233 | if (matchString(p, readername)) { |
234 | PrintAndLogEx(INFO, "Selected %s", p); |
235 | return p; |
236 | } |
237 | while (*p++ != '\0') ; // advance to next entry |
238 | } |
239 | |
240 | PrintAndLogEx(INFO, "No match. Using %s", PM3_SMARTCARD_DEFAULT_NAME); |
241 | return NULL; |
a9104f7e |
242 | } |
243 | |
41bdfce3 |
244 | |
a9104f7e |
245 | bool pcscSelectAlternativeCardReader(const char *readername) |
246 | { |
247 | DWORD readerlist_len; |
248 | LONG res = SCardListReaders(SC_Context, NULL, NULL, &readerlist_len); |
249 | if (res != SCARD_S_SUCCESS) { |
250 | return false; |
251 | } |
252 | |
253 | LPTSTR readerlist = calloc(readerlist_len, sizeof(char)); |
254 | res = SCardListReaders(SC_Context, NULL, readerlist, &readerlist_len); |
255 | if (res != SCARD_S_SUCCESS) { |
256 | free(readerlist); |
257 | return false; |
258 | } |
259 | |
260 | char *selected_readername = NULL; |
261 | if (readername) { |
41bdfce3 |
262 | selected_readername = matchReader(readerlist, readername); |
a9104f7e |
263 | } else { |
264 | selected_readername = pickReader(readerlist); |
265 | } |
266 | |
267 | if (selected_readername == NULL) { |
268 | free(readerlist); |
269 | return false; |
270 | } |
271 | |
272 | free(AlternativeSmartcardReader); |
273 | AlternativeSmartcardReader = malloc((strlen(selected_readername) + 1) * sizeof(char)); |
274 | strcpy(AlternativeSmartcardReader, selected_readername); |
41bdfce3 |
275 | |
276 | free(readerlist); |
a9104f7e |
277 | return true; |
278 | } |
279 | |
280 | |
281 | bool pcscGetATR(smart_card_atr_t *card) |
282 | { |
53fb848a |
283 | pcsc_clear_trace(); |
284 | pcsc_set_tracing(true); |
285 | |
a9104f7e |
286 | if (!card) { |
287 | return false; |
288 | } |
41bdfce3 |
289 | |
a9104f7e |
290 | card->atr_len = 0; |
291 | memset(card->atr, 0, sizeof(card->atr)); |
292 | |
293 | LONG res = SCardConnect(SC_Context, AlternativeSmartcardReader, SCARD_SHARE_SHARED, |
294 | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &SC_Card, &SC_Protocol); |
295 | if (res != SCARD_S_SUCCESS) { |
296 | return false; |
297 | } |
298 | |
299 | DWORD atr_len = sizeof(card->atr); |
300 | res = SCardGetAttrib(SC_Card, SCARD_ATTR_ATR_STRING, card->atr, &atr_len); |
301 | if (res != SCARD_S_SUCCESS) { |
302 | return false; |
303 | } |
304 | card->atr_len = atr_len; |
41bdfce3 |
305 | |
53fb848a |
306 | pcsc_LogTrace(card->atr, card->atr_len, 0, 0, false); |
307 | |
308 | pcsc_set_tracing(false); |
309 | |
41bdfce3 |
310 | return true; |
a9104f7e |
311 | } |
6b5105be |
312 | |
313 | |
314 | void pcscTransmit(uint8_t *data, uint32_t data_len, uint32_t flags, uint8_t *response, int *response_len) |
315 | { |
316 | LPCSCARD_IO_REQUEST protocol; |
317 | if (flags & SC_RAW_T0) { |
318 | protocol = SCARD_PCI_T0; |
319 | } else { |
320 | protocol = SCARD_PCI_RAW; |
321 | } |
322 | |
53fb848a |
323 | if ((flags & SC_CONNECT)) |
324 | pcsc_clear_trace(); |
6b5105be |
325 | |
53fb848a |
326 | pcsc_set_tracing(true); |
6b5105be |
327 | |
41bdfce3 |
328 | if ((flags & SC_CONNECT || flags & SC_SELECT)) { |
6b5105be |
329 | LONG res = SCardConnect(SC_Context, AlternativeSmartcardReader, SCARD_SHARE_SHARED, |
330 | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &SC_Card, &SC_Protocol); |
331 | if (res != SCARD_S_SUCCESS) { |
332 | *response_len = -1; |
333 | return; |
334 | } |
335 | } |
41bdfce3 |
336 | |
6b5105be |
337 | if ((flags & SC_RAW) || (flags & SC_RAW_T0)) { |
53fb848a |
338 | pcsc_LogTrace(data, data_len, 0, 0, true); |
6b5105be |
339 | DWORD len = *response_len; |
340 | LONG res = SCardTransmit(SC_Card, protocol, data, data_len, NULL, response, &len); |
341 | if (res != SCARD_S_SUCCESS) { |
342 | *response_len = -1; |
343 | } else { |
53fb848a |
344 | pcsc_LogTrace(response, len, 0, 0, false); |
6b5105be |
345 | *response_len = len; |
346 | } |
347 | } |
53fb848a |
348 | pcsc_set_tracing(false); |
6b5105be |
349 | } |