FreeRDP
Loading...
Searching...
No Matches
smartcard_virtual_gids.c
1
22#include <freerdp/config.h>
23
24#include <winpr/wlog.h>
25#include <winpr/stream.h>
26#include <winpr/collections.h>
27
28#include <freerdp/crypto/crypto.h>
29
30#include <zlib.h>
31
32#include "../../crypto/certificate.h"
33#include "../../crypto/privatekey.h"
34#include "smartcard_virtual_gids.h"
35
36#define TAG CHANNELS_TAG("smartcard.vgids")
37
38#define VGIDS_EFID_MASTER 0xA000
39#define VGIDS_EFID_COMMON 0xA010
40// #define VGIDS_EFID_CARDCF VGIDS_EFID_COMMON
41// #define VGIDS_EFID_CARDAPPS VGIDS_EFID_COMMON
42// #define VGIDS_EFID_CMAPFILE VGIDS_EFID_COMMON
43#define VGIDS_EFID_CARDID 0xA012
44// #define VGIDS_EFID_KXC00 VGIDS_EFID_COMMON
45#define VGIDS_EFID_CURRENTDF 0x3FFF
46
47#define VGIDS_DO_FILESYSTEMTABLE 0xDF1F
48#define VGIDS_DO_KEYMAP 0xDF20
49#define VGIDS_DO_CARDID 0xDF20
50#define VGIDS_DO_CARDAPPS 0xDF21
51#define VGIDS_DO_CARDCF 0xDF22
52#define VGIDS_DO_CMAPFILE 0xDF23
53#define VGIDS_DO_KXC00 0xDF24
54
55#define VGIDS_CARDID_SIZE 16
56#define VGIDS_MAX_PIN_SIZE 127
57
58#define VGIDS_DEFAULT_RETRY_COUNTER 3
59
60#define VGIDS_KEY_TYPE_KEYEXCHANGE 0x9A
61// #define VGIDS_KEY_TYPE_SIGNATURE 0x9C
62
63#define VGIDS_ALGID_RSA_1024 0x06
64#define VGIDS_ALGID_RSA_2048 0x07
65#define VGIDS_ALGID_RSA_3072 0x08
66#define VGIDS_ALGID_RSA_4096 0x09
67
68// #define VGIDS_SE_CRT_AUTH 0xA4
69#define VGIDS_SE_CRT_SIGN 0xB6
70#define VGIDS_SE_CRT_CONF 0xB8
71
72#define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40
73#define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80
74// #define VGIDS_SE_ALGOID_CT_RSA_1024 0x06
75// #define VGIDS_SE_ALGOID_CT_RSA_2048 0x07
76// #define VGIDS_SE_ALGOID_CT_RSA_3072 0x08
77// #define VGIDS_SE_ALGOID_CT_RSA_4096 0x09
78
79#define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40
80// #define VGIDS_SE_ALGOID_DST_RSA_1024 0x06
81// #define VGIDS_SE_ALGOID_DST_RSA_2048 0x07
82// #define VGIDS_SE_ALGOID_DST_RSA_3072 0x08
83// #define VGIDS_SE_ALGOID_DST_RSA_4096 0x09
84// #define VGIDS_SE_ALGOID_DST_ECDSA_P192 0x0A
85// #define VGIDS_SE_ALGOID_DST_ECDSA_P224 0x0B
86// #define VGIDS_SE_ALGOID_DST_ECDSA_P256 0x0C
87// #define VGIDS_SE_ALGOID_DST_ECDSA_P384 0x0D
88// #define VGIDS_SE_ALGOID_DST_ECDSA_P512 0x0E
89
90#define VGIDS_DEFAULT_KEY_REF 0x81
91
92#define ISO_INS_SELECT 0xA4
93#define ISO_INS_GETDATA 0xCB
94#define ISO_INS_GETRESPONSE 0xC0
95#define ISO_INS_MSE 0x22
96#define ISO_INS_PSO 0x2A
97#define ISO_INS_VERIFY 0x20
98
99#define ISO_STATUS_MORE_DATA 0x6100
100#define ISO_STATUS_VERIFYFAILED 0x6300
101#define ISO_STATUS_WRONGLC 0x6700
102#define ISO_STATUS_COMMANDNOTALLOWED 0x6900
103#define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982
104#define ISO_STATUS_AUTHMETHODBLOCKED 0x6983
105#define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80
106#define ISO_STATUS_FILENOTFOUND 0x6A82
107#define ISO_STATUS_INVALIDP1P2 0x6A86
108#define ISO_STATUS_INVALIDLC 0x6A87
109#define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88
110#define ISO_STATUS_SUCCESS 0x9000
111
112#define ISO_AID_MAX_SIZE 16
113
114#define ISO_FID_MF 0x3F00
115
116struct vgids_ef
117{
118 UINT16 id;
119 UINT16 dirID;
120 wStream* data;
121};
122typedef struct vgids_ef vgidsEF;
123
124struct vgids_se
125{
126 BYTE crt; /* control reference template tag */
127 BYTE algoId; /* Algorithm ID */
128 BYTE keyRef; /* Key reference */
129};
130typedef struct vgids_se vgidsSE;
131
132struct vgids_context
133{
134 UINT16 currentDF;
135 char* pin;
136 UINT16 curRetryCounter;
137 UINT16 retryCounter;
138 wStream* commandData;
139 wStream* responseData;
140 BOOL pinVerified;
141 vgidsSE currentSE;
142
143 rdpCertificate* certificate;
144 rdpPrivateKey* privateKey;
145
146 wArrayList* files;
147};
148
149/* PKCS 1.5 DER encoded digest information */
150#define VGIDS_MAX_DIGEST_INFO 7
151
152static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
153 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
154static const BYTE g_PKCS1_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
155 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
156static const BYTE g_PKCS1_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
157 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
158static const BYTE g_PKCS1_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
159 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
160static const BYTE g_PKCS1_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
161 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
162static const BYTE g_PKCS1_SHA512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60,
163 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
164 0x05, 0x05, 0x00, 0x04, 0x1c };
165static const BYTE g_PKCS1_SHA512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
166 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
167 0x06, 0x05, 0x00, 0x04, 0x20 };
168
169/* Helper struct to map PKCS1.5 digest info to OpenSSL EVP_MD */
170struct vgids_digest_info_map
171{
172 const BYTE* info;
173 size_t infoSize;
174 const EVP_MD* digest;
175};
176typedef struct vgids_digest_info_map vgidsDigestInfoMap;
177
178/* MS GIDS AID */
179/* xx: Used by the Windows smart card framework for the GIDS version number. This byte must be set
180 * to the GIDS specification revision number which is either 0x01 or 0x02.
181 * yy: Reserved for use by the card application (set to 01)
182 */
183static const BYTE g_MsGidsAID[] = {
184 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01
185};
186
187/* GIDS APP File Control Parameter:
188 FD-Byte (82): 38 (not shareable-DF)
189 Sec Attr (8C): 03 30 30 Create/Delete File(03) Ext/User-Auth (30)
190*/
191static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 };
192/* GIDS APP File Control Information:
193 AppID (4F, Len 0B): A0 00 00 03 97 42 54 46 59 02 01
194 Discretionary DOs (73, Len 03): 40 01 C0
195 Supported Auth Protocols (40, Len 01): C0 Mutual/External-Auth
196 */
197static const BYTE g_GidsAppFCI[] = { 0x61, 0x12, 0x4F, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42,
198 0x54, 0x46, 0x59, 0x02, 0x01, 0x73, 0x03, 0x40, 0x01, 0xC0 };
199
200/*
201typedef struct
202{
203 BYTE bVersion; // Cache version
204 BYTE bPinsFreshness; // Card PIN
205 WORD wContainersFreshness;
206 WORD wFilesFreshness;
207} CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; */
208static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 };
209
210/* {‘mscp’,0,0,0,0} */
211static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 };
212
213#pragma pack(push, 1)
214
215/* Type: CONTAINER_MAP_RECORD (taken from Windows Smart Card Minidriver Specification)
216
217 This structure describes the format of the Base CSP's
218 container map file, stored on the card. This is wellknown
219 logical file wszCONTAINER_MAP_FILE. The file consists of
220 zero or more of these records. */
221#define MAX_CONTAINER_NAME_LEN 39
222
223/* This flag is set in the CONTAINER_MAP_RECORD bFlags
224 member if the corresponding container is valid and currently
225 exists on the card. // If the container is deleted, its
226 bFlags field must be cleared. */
227#define CONTAINER_MAP_VALID_CONTAINER 1
228
229/* This flag is set in the CONTAINER_MAP_RECORD bFlags
230 member if the corresponding container is the default
231 container on the card. */
232#define CONTAINER_MAP_DEFAULT_CONTAINER 2
233
234struct vgids_container_map_entry
235{
236 WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1];
237 BYTE bFlags;
238 BYTE bReserved;
239 WORD wSigKeySizeBits;
240 WORD wKeyExchangeKeySizeBits;
241};
242typedef struct vgids_container_map_entry vgidsContainerMapEntry;
243
244struct vgids_filesys_table_entry
245{
246 char directory[9];
247 char filename[9];
248 UINT16 pad0;
249 UINT16 dataObjectIdentifier;
250 UINT16 pad1;
251 UINT16 fileIdentifier;
252 UINT16 unknown;
253};
254typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry;
255
256struct vgids_keymap_record
257{
258 UINT32 state;
259 BYTE algid;
260 BYTE keytype;
261 UINT16 keyref;
262 UINT16 unknownWithFFFF;
263 UINT16 unknownWith0000;
264};
265typedef struct vgids_keymap_record vgidsKeymapRecord;
266
267#pragma pack(pop)
268
269static void vgids_ef_free(void* ptr);
270
271static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT id)
272{
273 vgidsEF* ef = calloc(1, sizeof(vgidsEF));
274
275 ef->id = id;
276 ef->data = Stream_New(NULL, 1024);
277 if (!ef->data)
278 {
279 WLog_ERR(TAG, "Failed to create file data stream");
280 goto create_failed;
281 }
282 Stream_SetLength(ef->data, 0);
283
284 if (!ArrayList_Append(ctx->files, ef))
285 {
286 WLog_ERR(TAG, "Failed to add new ef to file list");
287 goto create_failed;
288 }
289
290 return ef;
291
292create_failed:
293 vgids_ef_free(ef);
294 return NULL;
295}
296
297static BOOL vgids_write_tlv(wStream* s, UINT16 tag, const void* data, size_t dataSize)
298{
299 WINPR_ASSERT(dataSize <= UINT16_MAX);
300
301 /* A maximum of 5 additional bytes is needed */
302 if (!Stream_EnsureRemainingCapacity(s, dataSize + 5))
303 {
304 WLog_ERR(TAG, "Failed to ensure capacity of DO stream");
305 return FALSE;
306 }
307
308 /* BER encoding: If the most-significant bit is set (0x80) the length is encoded in the
309 * remaining bits. So lengths < 128 bytes can be set directly, all others are encoded */
310 if (tag > 0xFF)
311 Stream_Write_UINT16_BE(s, tag);
312 else
313 Stream_Write_UINT8(s, (BYTE)tag);
314 if (dataSize < 128)
315 {
316 Stream_Write_UINT8(s, (BYTE)dataSize);
317 }
318 else if (dataSize < 256)
319 {
320 Stream_Write_UINT8(s, 0x81);
321 Stream_Write_UINT8(s, (BYTE)dataSize);
322 }
323 else
324 {
325 Stream_Write_UINT8(s, 0x82);
326 Stream_Write_UINT16_BE(s, (UINT16)dataSize);
327 }
328 Stream_Write(s, data, dataSize);
329 Stream_SealLength(s);
330 return TRUE;
331}
332
333static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID, const void* data, DWORD dataSize)
334{
335 /* Write DO to end of file: 2-Byte ID, 1-Byte Len, Data */
336 return vgids_write_tlv(ef->data, doID, data, dataSize);
337}
338
339static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)
340{
341 /* Read the given DO from the file: 2-Byte ID, 1-Byte Len, Data */
342 if (!Stream_SetPosition(ef->data, 0))
343 {
344 WLog_ERR(TAG, "Failed to seek to front of file");
345 return FALSE;
346 }
347
348 /* Look for the requested DO */
349 while (Stream_GetRemainingLength(ef->data) > 3)
350 {
351 BYTE len = 0;
352 size_t curPos = 0;
353 UINT16 doSize = 0;
354 UINT16 nextDOID = 0;
355
356 curPos = Stream_GetPosition(ef->data);
357 Stream_Read_UINT16_BE(ef->data, nextDOID);
358 Stream_Read_UINT8(ef->data, len);
359 if ((len & 0x80))
360 {
361 BYTE lenSize = len & 0x7F;
362 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))
363 return FALSE;
364
365 switch (lenSize)
366 {
367 case 1:
368 Stream_Read_UINT8(ef->data, doSize);
369 break;
370 case 2:
371 Stream_Read_UINT16_BE(ef->data, doSize);
372 break;
373 default:
374 WLog_ERR(TAG, "Unexpected tag length %" PRIu8, lenSize);
375 return FALSE;
376 }
377 }
378 else
379 doSize = len;
380
381 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))
382 return FALSE;
383
384 if (nextDOID == doID)
385 {
386 BYTE* outData = NULL;
387
388 /* Include Tag and length in result */
389 doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);
390 outData = malloc(doSize);
391 if (!outData)
392 {
393 WLog_ERR(TAG, "Failed to allocate output buffer");
394 return FALSE;
395 }
396
397 Stream_SetPosition(ef->data, curPos);
398 Stream_Read(ef->data, outData, doSize);
399 *data = outData;
400 *dataSize = doSize;
401 return TRUE;
402 }
403 else
404 {
405 /* Skip DO */
406 if (!Stream_SafeSeek(ef->data, doSize))
407 return FALSE;
408 }
409 }
410
411 return FALSE;
412}
413
414void vgids_ef_free(void* ptr)
415{
416 vgidsEF* ef = ptr;
417 if (ef)
418 {
419 Stream_Free(ef->data, TRUE);
420 free(ef);
421 }
422}
423
424static BOOL vgids_prepare_fstable(const vgidsFilesysTableEntry* fstable, DWORD numEntries,
425 BYTE** outData, DWORD* outDataSize)
426{
427 /* Filesystem table:
428 BYTE unknown: 0x01
429 Array of vgidsFilesysTableEntry
430 */
431 BYTE* data = malloc(sizeof(vgidsFilesysTableEntry) * numEntries + 1);
432 if (!data)
433 {
434 WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
435 return FALSE;
436 }
437
438 *data = 0x01;
439 for (UINT32 i = 0; i < numEntries; ++i)
440 memcpy(data + 1 + (sizeof(vgidsFilesysTableEntry) * i), &fstable[i],
441 sizeof(vgidsFilesysTableEntry));
442
443 *outData = data;
444 *outDataSize = sizeof(vgidsFilesysTableEntry) * numEntries + 1;
445
446 return TRUE;
447}
448
449static BOOL vgids_prepare_certificate(const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)
450{
451 /* Key exchange container:
452 UINT16 compression version: 0001
453 UINT16 source size
454 ZLIB compressed cert
455 */
456 uLongf destSize = 0;
457 wStream* s = NULL;
458 BYTE* comprData = NULL;
459
460 WINPR_ASSERT(cert);
461
462 size_t certSize = 0;
463 BYTE* certData = freerdp_certificate_get_der(cert, &certSize);
464 if (!certData || (certSize == 0) || (certSize > UINT16_MAX))
465 {
466 WLog_ERR(TAG, "Failed to get certificate size");
467 goto handle_error;
468 }
469
470 comprData = malloc(certSize);
471 if (!comprData)
472 {
473 WLog_ERR(TAG, "Failed to allocate certificate buffer");
474 goto handle_error;
475 }
476
477 /* compress certificate data */
478 destSize = WINPR_ASSERTING_INT_CAST(uint16_t, certSize);
479 if (compress(comprData, &destSize, certData, WINPR_ASSERTING_INT_CAST(uint16_t, certSize)) !=
480 Z_OK)
481 {
482 WLog_ERR(TAG, "Failed to compress certificate data");
483 goto handle_error;
484 }
485
486 /* Write container data */
487 s = Stream_New(NULL, destSize + 4);
488 Stream_Write_UINT16(s, 0x0001);
489 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, certSize));
490 Stream_Write(s, comprData, destSize);
491 Stream_SealLength(s);
492
493 *kxc = Stream_Buffer(s);
494 *kxcSize = (DWORD)Stream_Length(s);
495
496 Stream_Free(s, FALSE);
497 free(certData);
498 free(comprData);
499 return TRUE;
500
501handle_error:
502 Stream_Free(s, TRUE);
503 free(certData);
504 free(comprData);
505 return FALSE;
506}
507
508static size_t get_rsa_key_size(const rdpPrivateKey* privateKey)
509{
510 WINPR_ASSERT(privateKey);
511
512 return freerdp_key_get_bits(privateKey) / 8;
513}
514
515static BYTE vgids_get_algid(vgidsContext* p_Ctx)
516{
517 WINPR_ASSERT(p_Ctx);
518
519 switch (get_rsa_key_size(p_Ctx->privateKey))
520 {
521 case (1024 / 8):
522 return VGIDS_ALGID_RSA_1024;
523 case (2048 / 8):
524 return VGIDS_ALGID_RSA_2048;
525 case (3072 / 8):
526 return VGIDS_ALGID_RSA_3072;
527 case (4096 / 8):
528 return VGIDS_ALGID_RSA_4096;
529 default:
530 WLog_ERR(TAG, "Failed to determine algid for private key");
531 break;
532 }
533
534 return 0;
535}
536
537static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)
538{
539 /* Key map record table:
540 BYTE unknown (count?): 0x01
541 Array of vgidsKeymapRecord
542 */
543 BYTE* data = NULL;
544 vgidsKeymapRecord record = {
545 1, /* state */
546 0, /* algo */
547 VGIDS_KEY_TYPE_KEYEXCHANGE, /* keytpe */
548 (0xB000 | VGIDS_DEFAULT_KEY_REF), /* keyref */
549 0xFFFF, /* unknown FFFF */
550 0x0000 /* unknown 0000 */
551 };
552
553 /* Determine algo */
554 BYTE algid = vgids_get_algid(context);
555 if (algid == 0)
556 return FALSE;
557
558 data = malloc(sizeof(record) + 1);
559 if (!data)
560 {
561 WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
562 return FALSE;
563 }
564
565 *data = 0x01;
566 record.algid = algid;
567 memcpy(data + 1, &record, sizeof(record));
568
569 *outData = data;
570 *outDataSize = sizeof(record) + 1;
571
572 return TRUE;
573}
574
575static BOOL vgids_parse_apdu_header(wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,
576 BYTE* le)
577{
578 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
579 return FALSE;
580
581 /* Read and verify APDU data */
582 if (cla)
583 Stream_Read_UINT8(s, *cla);
584 else
585 Stream_Seek(s, 1);
586 if (ins)
587 Stream_Read_UINT8(s, *ins);
588 else
589 Stream_Seek(s, 1);
590 if (p1)
591 Stream_Read_UINT8(s, *p1);
592 else
593 Stream_Seek(s, 1);
594 if (p2)
595 Stream_Read_UINT8(s, *p2);
596 else
597 Stream_Seek(s, 1);
598
599 /* If LC is requested - check remaining length and read as well */
600 if (lc)
601 {
602 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
603 return FALSE;
604
605 Stream_Read_UINT8(s, *lc);
606 if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))
607 return FALSE;
608 }
609
610 /* read LE */
611 if (le)
612 {
613 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
614 return FALSE;
615 Stream_Read_UINT8(s, *le);
616 }
617
618 return TRUE;
619}
620
621static BOOL vgids_create_response(UINT16 status, const BYTE* answer, DWORD answerSize,
622 BYTE** outData, DWORD* outDataSize)
623{
624 BYTE* out = malloc(answerSize + 2);
625 if (!out)
626 {
627 WLog_ERR(TAG, "Failed to allocate memory for response data");
628 return FALSE;
629 }
630
631 *outData = out;
632 if (answer)
633 {
634 memcpy(out, answer, answerSize);
635 out += answerSize;
636 }
637
638 *out = (BYTE)((status >> 8) & 0xFF);
639 *(out + 1) = (BYTE)(status & 0xFF);
640 *outDataSize = answerSize + 2;
641 return TRUE;
642}
643
644static BOOL vgids_read_do_fkt(void* data, size_t index, va_list ap)
645{
646 BYTE* response = NULL;
647 DWORD responseSize = 0;
648 vgidsEF* file = (vgidsEF*)data;
649 vgidsContext* context = va_arg(ap, vgidsContext*);
650 UINT16 efID = (UINT16)va_arg(ap, unsigned);
651 UINT16 doID = (UINT16)va_arg(ap, unsigned);
652 WINPR_UNUSED(index);
653
654 if (efID == 0x3FFF || efID == file->id)
655 {
656 /* If the DO was successfully read - abort file enum */
657 if (vgids_ef_read_do(file, doID, &response, &responseSize))
658 {
659 context->responseData = Stream_New(response, (size_t)responseSize);
660 return FALSE;
661 }
662 }
663
664 return TRUE;
665}
666
667static void vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
668{
669 ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);
670}
671
672static void vgids_reset_context_response(vgidsContext* context)
673{
674 Stream_Free(context->responseData, TRUE);
675 context->responseData = NULL;
676}
677
678static void vgids_reset_context_command_data(vgidsContext* context)
679{
680 Stream_Free(context->commandData, TRUE);
681 context->commandData = NULL;
682}
683
684static BOOL vgids_ins_select(vgidsContext* context, wStream* s, BYTE** response,
685 DWORD* responseSize)
686{
687 BYTE p1 = 0;
688 BYTE p2 = 0;
689 BYTE lc = 0;
690 DWORD resultDataSize = 0;
691 const BYTE* resultData = NULL;
692 UINT16 status = ISO_STATUS_SUCCESS;
693
694 /* The only select operations performed are either select by AID or select 3FFF (return
695 * information about the currently selected DF) */
696 if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
697 return FALSE;
698
699 /* Check P1 for selection mode */
700 switch (p1)
701 {
702 /* Select by AID */
703 case 0x04:
704 {
705 /* read AID from APDU */
706 BYTE aid[ISO_AID_MAX_SIZE] = { 0 };
707 if (lc > ISO_AID_MAX_SIZE)
708 {
709 WLog_ERR(TAG, "The LC byte is greater than the maximum AID length");
710 status = ISO_STATUS_INVALIDLC;
711 break;
712 }
713
714 /* Check if we select MS GIDS App (only one we know) */
715 Stream_Read(s, aid, lc);
716 if (memcmp(aid, g_MsGidsAID, lc) != 0)
717 {
718 status = ISO_STATUS_FILENOTFOUND;
719 break;
720 }
721
722 /* Return FCI or FCP for MsGids App */
723 switch (p2)
724 {
725 /* Return FCI information */
726 case 0x00:
727 {
728 resultData = g_GidsAppFCI;
729 resultDataSize = sizeof(g_GidsAppFCI);
730 break;
731 }
732 /* Return FCP information */
733 case 0x04:
734 {
735 resultData = g_GidsAppFCP;
736 resultDataSize = sizeof(g_GidsAppFCP);
737 break;
738 }
739 default:
740 status = ISO_STATUS_INVALIDP1P2;
741 break;
742 }
743
744 if (resultData)
745 context->currentDF = ISO_FID_MF;
746 break;
747 }
748 /* Select by FID */
749 case 0x00:
750 {
751 /* read FID from APDU */
752 UINT16 fid = 0;
753 if (lc > 2)
754 {
755 WLog_ERR(TAG, "The LC byte for the file ID is greater than 2");
756 status = ISO_STATUS_INVALIDLC;
757 break;
758 }
759
760 Stream_Read_UINT16_BE(s, fid);
761 if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)
762 {
763 status = ISO_STATUS_FILENOTFOUND;
764 break;
765 }
766 break;
767 }
768 default:
769 {
770 /* P1 P2 combination not supported */
771 status = ISO_STATUS_INVALIDP1P2;
772 break;
773 }
774 }
775
776 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
777}
778
779static UINT16 vgids_handle_chained_response(vgidsContext* context, const BYTE** response,
780 DWORD* responseSize)
781{
782 /* Cap to a maximum of 256 bytes and set status to more data */
783 UINT16 status = ISO_STATUS_SUCCESS;
784 DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);
785 if (remainingBytes > 256)
786 {
787 status = ISO_STATUS_MORE_DATA;
788 remainingBytes = 256;
789 }
790
791 *response = Stream_Buffer(context->responseData);
792 *responseSize = remainingBytes;
793 Stream_Seek(context->responseData, remainingBytes);
794
795 /* Check if there are more than 256 bytes left or if we can already provide the remaining length
796 * in the status word */
797 remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);
798 if (remainingBytes < 256 && remainingBytes != 0)
799 status |= (remainingBytes & 0xFF);
800 return status;
801}
802
803static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)
804{
805 BOOL rc = FALSE;
806 wStream* pubKey = NULL;
807 wStream* response = NULL;
808
809 WINPR_ASSERT(context);
810
811 /* Get key components */
812 size_t nSize = 0;
813 size_t eSize = 0;
814
815 char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);
816 char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);
817
818 if (!n || !e)
819 goto handle_error;
820
821 pubKey = Stream_New(NULL, nSize + eSize + 0x10);
822 if (!pubKey)
823 {
824 WLog_ERR(TAG, "Failed to allocate public key stream");
825 goto handle_error;
826 }
827
828 response = Stream_New(NULL, Stream_Capacity(pubKey) + 0x10);
829 if (!response)
830 {
831 WLog_ERR(TAG, "Failed to allocate response stream");
832 goto handle_error;
833 }
834
835 /* write modulus and exponent DOs */
836 if (!vgids_write_tlv(pubKey, 0x81, n, nSize))
837 goto handle_error;
838
839 if (!vgids_write_tlv(pubKey, 0x82, e, eSize))
840 goto handle_error;
841
842 /* write ISO public key template */
843 if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))
844 goto handle_error;
845
846 /* set response data */
847 Stream_SetPosition(response, 0);
848 context->responseData = response;
849 response = NULL;
850
851 rc = TRUE;
852handle_error:
853 free(n);
854 free(e);
855 Stream_Free(pubKey, TRUE);
856 Stream_Free(response, TRUE);
857 return rc;
858}
859
860static BOOL vgids_ins_getdata(vgidsContext* context, wStream* s, BYTE** response,
861 DWORD* responseSize)
862{
863 UINT16 doId = 0;
864 UINT16 fileId = 0;
865 BYTE p1 = 0;
866 BYTE p2 = 0;
867 BYTE lc = 0;
868 DWORD resultDataSize = 0;
869 const BYTE* resultData = NULL;
870 UINT16 status = ISO_STATUS_SUCCESS;
871
872 /* GetData is called a lot!
873 - To retrieve DOs from files
874 - To retrieve public key information
875 */
876 if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
877 return FALSE;
878
879 /* free any previous queried data */
880 vgids_reset_context_response(context);
881
882 /* build up file identifier */
883 fileId = (UINT16)(((UINT16)p1 << 8) | p2);
884
885 /* Do we have a DO reference? */
886 switch (lc)
887 {
888 case 4:
889 {
890 BYTE tag = 0;
891 BYTE length = 0;
892 Stream_Read_UINT8(s, tag);
893 Stream_Read_UINT8(s, length);
894 if (tag != 0x5C && length != 0x02)
895 {
896 status = ISO_STATUS_INVALIDCOMMANDDATA;
897 break;
898 }
899
900 Stream_Read_UINT16_BE(s, doId);
901 vgids_read_do(context, fileId, doId);
902 break;
903 }
904 case 0xA:
905 {
906 UINT16 pubKeyDO = 0;
907 BYTE tag = 0;
908 BYTE length = 0;
909 BYTE keyRef = 0;
910
911 /* We want to retrieve the public key? */
912 if (p1 != 0x3F && p2 != 0xFF)
913 {
914 status = ISO_STATUS_INVALIDP1P2;
915 break;
916 }
917
918 /* read parent tag/length */
919 Stream_Read_UINT8(s, tag);
920 Stream_Read_UINT8(s, length);
921 if (tag != 0x70 || length != 0x08)
922 {
923 status = ISO_STATUS_INVALIDCOMMANDDATA;
924 break;
925 }
926
927 /* read key reference TLV */
928 Stream_Read_UINT8(s, tag);
929 Stream_Read_UINT8(s, length);
930 Stream_Read_UINT8(s, keyRef);
931 if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
932 {
933 status = ISO_STATUS_INVALIDCOMMANDDATA;
934 break;
935 }
936
937 /* read key value template TLV */
938 Stream_Read_UINT8(s, tag);
939 Stream_Read_UINT8(s, length);
940 if (tag != 0xA5 || length != 0x03)
941 {
942 status = ISO_STATUS_INVALIDCOMMANDDATA;
943 break;
944 }
945
946 Stream_Read_UINT16_BE(s, pubKeyDO);
947 Stream_Read_UINT8(s, length);
948 if (pubKeyDO != 0x7F49 || length != 0x80)
949 {
950 status = ISO_STATUS_INVALIDCOMMANDDATA;
951 break;
952 }
953
954 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
955 {
956 status = ISO_STATUS_INVALIDLC;
957 break;
958 }
959
960 /* Return public key value */
961 vgids_get_public_key(context, pubKeyDO);
962 break;
963 }
964 default:
965 status = ISO_STATUS_INVALIDCOMMANDDATA;
966 break;
967 }
968
969 /* If we have response data, make it ready for return */
970 if (context->responseData)
971 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
972 else if (status == ISO_STATUS_SUCCESS)
973 status = ISO_STATUS_REFERENCEDATANOTFOUND;
974
975 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
976}
977
978static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s,
979 BYTE** response, DWORD* responseSize)
980{
981 BYTE tag = 0;
982 BYTE length = 0;
983 BYTE p1 = 0;
984 BYTE p2 = 0;
985 BYTE lc = 0;
986 DWORD resultDataSize = 0;
987 const BYTE* resultData = NULL;
988 UINT16 status = ISO_STATUS_SUCCESS;
989
990 vgids_reset_context_command_data(context);
991 vgids_reset_context_response(context);
992
993 /* Manage security environment prepares the card for performing crypto operations. */
994 if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
995 return FALSE;
996
997 /* Check APDU params */
998 /* P1: Set Computation, decipherment, Internal Auth */
999 /* P2: Digital Signature (B6), Confidentiality (B8) */
1000 if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
1001 {
1002 status = ISO_STATUS_INVALIDP1P2;
1003 goto create_response;
1004 }
1005
1006 if (lc != 6)
1007 {
1008 status = ISO_STATUS_WRONGLC;
1009 goto create_response;
1010 }
1011
1012 context->currentSE.crt = p2;
1013
1014 /* parse command buffer */
1015 /* Read algo ID */
1016 Stream_Read_UINT8(s, tag);
1017 Stream_Read_UINT8(s, length);
1018 if (tag != 0x80 || length != 0x01)
1019 {
1020 status = ISO_STATUS_INVALIDCOMMANDDATA;
1021 goto create_response;
1022 }
1023 Stream_Read_UINT8(s, context->currentSE.algoId);
1024
1025 /* Read private key reference */
1026 Stream_Read_UINT8(s, tag);
1027 Stream_Read_UINT8(s, length);
1028 if (tag != 0x84 || length != 0x01)
1029 {
1030 status = ISO_STATUS_INVALIDCOMMANDDATA;
1031 goto create_response;
1032 }
1033 Stream_Read_UINT8(s, context->currentSE.keyRef);
1034
1035create_response:
1036 /* If an error occurred reset SE */
1037 if (status != ISO_STATUS_SUCCESS)
1038 memset(&context->currentSE, 0, sizeof(context->currentSE));
1039 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1040}
1041
1042static BOOL vgids_perform_digital_signature(vgidsContext* context)
1043{
1044 size_t sigSize = 0;
1045 size_t msgSize = 0;
1046 EVP_PKEY_CTX* ctx = NULL;
1047 EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
1048 const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
1049 { g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() },
1050 { g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() },
1051 { g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() },
1052 { g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() },
1053 { g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() },
1054#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1055 { g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
1056 { g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
1057#endif
1058 };
1059
1060 if (!pk)
1061 {
1062 WLog_ERR(TAG, "Failed to create PKEY");
1063 return FALSE;
1064 }
1065
1066 vgids_reset_context_response(context);
1067
1068 /* for each digest info */
1069 Stream_SetPosition(context->commandData, 0);
1070 for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
1071 {
1072 /* have we found our digest? */
1073 const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
1074 if (Stream_Length(context->commandData) >= digest->infoSize &&
1075 memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
1076 {
1077 /* skip digest info and calculate message size */
1078 Stream_Seek(context->commandData, digest->infoSize);
1079 if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
1080 goto sign_failed;
1081 msgSize = Stream_GetRemainingLength(context->commandData);
1082
1083 /* setup signing context */
1084 ctx = EVP_PKEY_CTX_new(pk, NULL);
1085 if (!ctx)
1086 {
1087 WLog_ERR(TAG, "Failed to create signing context");
1088 goto sign_failed;
1089 }
1090
1091 if (EVP_PKEY_sign_init(ctx) <= 0)
1092 {
1093 WLog_ERR(TAG, "Failed to init signing context");
1094 goto sign_failed;
1095 }
1096
1097 /* set padding and signature algo */
1098 if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
1099 {
1100 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
1101 {
1102 WLog_ERR(TAG, "Failed to set padding mode");
1103 goto sign_failed;
1104 }
1105 }
1106
1107 if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
1108 {
1109 WLog_ERR(TAG, "Failed to set signing mode");
1110 goto sign_failed;
1111 }
1112
1113 /* Determine buffer length */
1114 if (EVP_PKEY_sign(ctx, NULL, &sigSize, Stream_Pointer(context->commandData), msgSize) <=
1115 0)
1116 {
1117 WLog_ERR(TAG, "Failed to determine signature size");
1118 goto sign_failed;
1119 }
1120
1121 context->responseData = Stream_New(NULL, sigSize);
1122 if (!context->responseData)
1123 {
1124 WLog_ERR(TAG, "Failed to allocate signing buffer");
1125 goto sign_failed;
1126 }
1127
1128 /* sign */
1129 if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
1130 Stream_Pointer(context->commandData), msgSize) <= 0)
1131 {
1132 WLog_ERR(TAG, "Failed to create signature");
1133 goto sign_failed;
1134 }
1135
1136 Stream_SetLength(context->responseData, sigSize);
1137 EVP_PKEY_CTX_free(ctx);
1138 break;
1139 }
1140 }
1141
1142 EVP_PKEY_free(pk);
1143 vgids_reset_context_command_data(context);
1144 return TRUE;
1145
1146sign_failed:
1147 vgids_reset_context_command_data(context);
1148 vgids_reset_context_response(context);
1149 EVP_PKEY_CTX_free(ctx);
1150 EVP_PKEY_free(pk);
1151 return FALSE;
1152}
1153
1154static BOOL vgids_perform_decrypt(vgidsContext* context)
1155{
1156 EVP_PKEY_CTX* ctx = NULL;
1157 BOOL rc = FALSE;
1158 int res = 0;
1159 int padding = RSA_NO_PADDING;
1160
1161 vgids_reset_context_response(context);
1162
1163 /* determine padding */
1164 if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
1165 padding = RSA_PKCS1_PADDING;
1166 else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
1167 padding = RSA_PKCS1_OAEP_PADDING;
1168
1169 /* init response buffer */
1170 EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
1171 if (!pkey)
1172 goto decrypt_failed;
1173 ctx = EVP_PKEY_CTX_new(pkey, NULL);
1174 if (!ctx)
1175 goto decrypt_failed;
1176 if (EVP_PKEY_decrypt_init(ctx) <= 0)
1177 goto decrypt_failed;
1178 if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
1179 goto decrypt_failed;
1180
1181 /* Determine buffer length */
1182 const size_t inlen = Stream_Length(context->commandData);
1183 size_t outlen = 0;
1184 res = EVP_PKEY_decrypt(ctx, NULL, &outlen, Stream_Buffer(context->commandData), inlen);
1185 if (res < 0)
1186 {
1187 WLog_ERR(TAG, "Failed to decrypt data");
1188 goto decrypt_failed;
1189 }
1190
1191 /* Prepare output buffer */
1192 context->responseData = Stream_New(NULL, outlen);
1193 if (!context->responseData)
1194 {
1195 WLog_ERR(TAG, "Failed to create decryption buffer");
1196 goto decrypt_failed;
1197 }
1198
1199 /* Decrypt */
1200 res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1201 Stream_Buffer(context->commandData), inlen);
1202
1203 if (res < 0)
1204 {
1205 WLog_ERR(TAG, "Failed to decrypt data");
1206 goto decrypt_failed;
1207 }
1208
1209 Stream_SetLength(context->responseData, outlen);
1210 rc = TRUE;
1211
1212decrypt_failed:
1213 EVP_PKEY_CTX_free(ctx);
1214 EVP_PKEY_free(pkey);
1215 vgids_reset_context_command_data(context);
1216 if (!rc)
1217 vgids_reset_context_response(context);
1218 return rc;
1219}
1220
1221static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,
1222 DWORD* responseSize)
1223{
1224 BYTE cla = 0;
1225 BYTE p1 = 0;
1226 BYTE p2 = 0;
1227 BYTE lc = 0;
1228 DWORD resultDataSize = 0;
1229 const BYTE* resultData = NULL;
1230 UINT16 status = ISO_STATUS_SUCCESS;
1231
1232 /* Perform security operation */
1233 if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL))
1234 return FALSE;
1235
1236 if (lc == 0)
1237 {
1238 status = ISO_STATUS_WRONGLC;
1239 goto create_response;
1240 }
1241
1242 /* Is our default key referenced? */
1243 if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1244 {
1245 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1246 goto create_response;
1247 }
1248
1249 /* is the pin protecting the key verified? */
1250 if (!context->pinVerified)
1251 {
1252 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1253 goto create_response;
1254 }
1255
1256 /* Append the data to the context command buffer (PSO might chain command data) */
1257 if (!context->commandData)
1258 {
1259 context->commandData = Stream_New(NULL, lc);
1260 if (!context->commandData)
1261 return FALSE;
1262 }
1263 else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1264 return FALSE;
1265
1266 Stream_Write(context->commandData, Stream_Pointer(s), lc);
1267 Stream_SealLength(context->commandData);
1268
1269 /* Check if the correct operation is requested for our current SE */
1270 switch (context->currentSE.crt)
1271 {
1272 case VGIDS_SE_CRT_SIGN:
1273 {
1274 if (p1 != 0x9E || p2 != 0x9A)
1275 {
1276 status = ISO_STATUS_INVALIDP1P2;
1277 break;
1278 }
1279
1280 /* If chaining is over perform op */
1281 if (!(cla & 0x10))
1282 vgids_perform_digital_signature(context);
1283 break;
1284 }
1285 case VGIDS_SE_CRT_CONF:
1286 {
1287 if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1288 {
1289 status = ISO_STATUS_INVALIDP1P2;
1290 break;
1291 }
1292
1293 /* If chaining is over perform op */
1294 if (!(cla & 0x10))
1295 vgids_perform_decrypt(context);
1296 break;
1297 }
1298 default:
1299 status = ISO_STATUS_INVALIDP1P2;
1300 break;
1301 }
1302
1303 /* Do chaining of response data if necessary */
1304 if (status == ISO_STATUS_SUCCESS && context->responseData)
1305 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1306
1307 /* Check APDU params */
1308create_response:
1309 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1310}
1311
1312static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,
1313 DWORD* responseSize)
1314{
1315 BYTE p1 = 0;
1316 BYTE p2 = 0;
1317 BYTE le = 0;
1318 DWORD resultDataSize = 0;
1319 const BYTE* resultData = NULL;
1320 DWORD expectedLen = 0;
1321 DWORD remainingSize = 0;
1322 UINT16 status = ISO_STATUS_SUCCESS;
1323
1324 /* Get response continues data transfer after a previous get data command */
1325 /* Check if there is any data to transfer left */
1326 if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1327 {
1328 status = ISO_STATUS_COMMANDNOTALLOWED;
1329 goto create_response;
1330 }
1331
1332 if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le))
1333 return FALSE;
1334
1335 /* Check APDU params */
1336 if (p1 != 00 || p2 != 0x00)
1337 {
1338 status = ISO_STATUS_INVALIDP1P2;
1339 goto create_response;
1340 }
1341
1342 /* LE = 0 means 256 bytes expected */
1343 expectedLen = le;
1344 if (expectedLen == 0)
1345 expectedLen = 256;
1346
1347 /* prepare response size and update offset */
1348 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1349 if (remainingSize < expectedLen)
1350 expectedLen = remainingSize;
1351
1352 resultData = Stream_Pointer(context->responseData);
1353 resultDataSize = expectedLen;
1354 Stream_Seek(context->responseData, expectedLen);
1355
1356 /* If more data is left return 61XX - otherwise 9000 */
1357 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1358 if (remainingSize > 0)
1359 {
1360 status = ISO_STATUS_MORE_DATA;
1361 if (remainingSize < 256)
1362 status |= (remainingSize & 0xFF);
1363 }
1364
1365create_response:
1366 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1367}
1368
1369static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,
1370 DWORD* responseSize)
1371{
1372 BYTE ins = 0;
1373 BYTE p1 = 0;
1374 BYTE p2 = 0;
1375 BYTE lc = 0;
1376 UINT16 status = ISO_STATUS_SUCCESS;
1377 char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 };
1378
1379 /* Verify is always called for the application password (PIN) P2=0x80 */
1380 if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL))
1381 return FALSE;
1382
1383 /* Check APDU params */
1384 if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1385 {
1386 status = ISO_STATUS_INVALIDP1P2;
1387 goto create_response;
1388 }
1389
1390 /* shall we reset the security state? */
1391 if (p2 == 0x82)
1392 {
1393 context->pinVerified = FALSE;
1394 goto create_response;
1395 }
1396
1397 /* Check if pin is not already blocked */
1398 if (context->curRetryCounter == 0)
1399 {
1400 status = ISO_STATUS_AUTHMETHODBLOCKED;
1401 goto create_response;
1402 }
1403
1404 /* Read and verify LC */
1405 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1406 {
1407 status = ISO_STATUS_INVALIDLC;
1408 goto create_response;
1409 }
1410
1411 Stream_Read_UINT8(s, lc);
1412 if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1413 {
1414 status = ISO_STATUS_INVALIDLC;
1415 goto create_response;
1416 }
1417
1418 /* read and verify pin */
1419 Stream_Read(s, pin, lc);
1420 if (strcmp(context->pin, pin) != 0)
1421 {
1422 /* retries are encoded in the lowest 4-bit of the status code */
1423 --context->curRetryCounter;
1424 context->pinVerified = FALSE;
1425 status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
1426 }
1427 else
1428 {
1429 /* reset retry counter and mark pin as verified */
1430 context->curRetryCounter = context->retryCounter;
1431 context->pinVerified = TRUE;
1432 }
1433
1434create_response:
1435 return vgids_create_response(status, NULL, 0, response, responseSize);
1436}
1437
1438vgidsContext* vgids_new(void)
1439{
1440 wObject* obj = NULL;
1441 vgidsContext* ctx = calloc(1, sizeof(vgidsContext));
1442
1443 ctx->files = ArrayList_New(FALSE);
1444 if (!ctx->files)
1445 {
1446 WLog_ERR(TAG, "Failed to create files array list");
1447 goto create_failed;
1448 }
1449
1450 obj = ArrayList_Object(ctx->files);
1451 obj->fnObjectFree = vgids_ef_free;
1452
1453 return ctx;
1454
1455create_failed:
1456 vgids_free(ctx);
1457 return NULL;
1458}
1459
1460BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)
1461{
1462 DWORD kxcSize = 0;
1463 DWORD keymapSize = 0;
1464 DWORD fsTableSize = 0;
1465 BOOL rc = FALSE;
1466 BYTE* kxc = NULL;
1467 BYTE* keymap = NULL;
1468 BYTE* fsTable = NULL;
1469 vgidsEF* masterEF = NULL;
1470 vgidsEF* cardidEF = NULL;
1471 vgidsEF* commonEF = NULL;
1472 BYTE cardid[VGIDS_CARDID_SIZE] = { 0 };
1473 vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ',
1474 '0', '0' },
1475 CONTAINER_MAP_VALID_CONTAINER |
1476 CONTAINER_MAP_DEFAULT_CONTAINER,
1477 0,
1478 0,
1479 0x00 /* key-size in bits - filled out later */ };
1480 vgidsFilesysTableEntry filesys[] = {
1481 { "mscp", "", 0, 0, 0, 0xA000, 0 },
1482 { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 },
1483 { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1484 { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1485 { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1486 { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1487 };
1488
1489 /* Check params */
1490 if (!cert || !privateKey || !pin)
1491 {
1492 WLog_DBG(TAG, "Passed invalid NULL argument: cert=%p, privateKey=%p, pin=%p", cert,
1493 privateKey, pin);
1494 goto init_failed;
1495 }
1496
1497 /* Convert PEM input to DER certificate/public key/private key */
1498 ctx->certificate = freerdp_certificate_new_from_pem(cert);
1499 if (!ctx->certificate)
1500 goto init_failed;
1501
1502 ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey, NULL);
1503 if (!ctx->privateKey)
1504 goto init_failed;
1505
1506 /* create masterfile */
1507 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1508 masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1509 if (!masterEF)
1510 goto init_failed;
1511
1512 /* create cardid file with cardid DO */
1513 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1514 cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1515 if (!cardidEF)
1516 goto init_failed;
1517 winpr_RAND(cardid, sizeof(cardid));
1518 if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))
1519 goto init_failed;
1520
1521 /* create user common file */
1522 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1523 commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1524 if (!commonEF)
1525 goto init_failed;
1526
1527 /* write card cache DO */
1528 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))
1529 goto init_failed;
1530
1531 /* write container map DO */
1532 const size_t size = get_rsa_key_size(ctx->privateKey);
1533 if ((size == 0) || (size > UINT16_MAX / 8))
1534 goto init_failed;
1535
1536 cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1537 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))
1538 goto init_failed;
1539
1540 /* write cardapps DO */
1541 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1542 sizeof(g_CardAppsContents)))
1543 goto init_failed;
1544
1545 /* convert and write certificate to key exchange container */
1546 if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1547 goto init_failed;
1548 if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1549 goto init_failed;
1550
1551 /* prepare and write file system table */
1552 if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1553 goto init_failed;
1554 if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1555 goto init_failed;
1556
1557 /* vgids_prepare_keymap and write to masterEF */
1558 if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1559 goto init_failed;
1560 if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1561 goto init_failed;
1562
1563 /* store user pin */
1564 ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
1565 ctx->pin = _strdup(pin);
1566 if (!ctx->pin)
1567 goto init_failed;
1568
1569 rc = TRUE;
1570
1571init_failed:
1572 // ArrayList_Append in vgids_ef_new takes ownership
1573 // of cardidEF, commonEF, masterEF
1574 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1575 free(kxc);
1576 free(keymap);
1577 free(fsTable);
1578 return rc;
1579}
1580
1581BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,
1582 DWORD* responseSize)
1583{
1584 wStream s;
1585 static int x = 1;
1586
1587 /* Check params */
1588 if (!context || !data || !response || !responseSize)
1589 {
1590 WLog_ERR(TAG, "Invalid NULL pointer passed");
1591 return FALSE;
1592 }
1593
1594 if (dataSize < 4)
1595 {
1596 WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1597 return FALSE;
1598 }
1599
1600 /* Examine INS byte */
1601 Stream_StaticConstInit(&s, data, dataSize);
1602 if (x++ == 0xe)
1603 x = 0xe + 1;
1604 switch (data[1])
1605 {
1606 case ISO_INS_SELECT:
1607 return vgids_ins_select(context, &s, response, responseSize);
1608 case ISO_INS_GETDATA:
1609 return vgids_ins_getdata(context, &s, response, responseSize);
1610 case ISO_INS_GETRESPONSE:
1611 return vgids_ins_getresponse(context, &s, response, responseSize);
1612 case ISO_INS_MSE:
1613 return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1614 case ISO_INS_PSO:
1615 return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1616 case ISO_INS_VERIFY:
1617 return vgids_ins_verify(context, &s, response, responseSize);
1618 default:
1619 break;
1620 }
1621
1622 /* return command not allowed */
1623 return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize);
1624}
1625
1626void vgids_free(vgidsContext* context)
1627{
1628 if (context)
1629 {
1630 freerdp_key_free(context->privateKey);
1631 freerdp_certificate_free(context->certificate);
1632 Stream_Free(context->commandData, TRUE);
1633 Stream_Free(context->responseData, TRUE);
1634 free(context->pin);
1635 ArrayList_Free(context->files);
1636 free(context);
1637 }
1638}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57