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