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 {
1183 const size_t inlen = Stream_Length(context->commandData);
1184 size_t outlen = 0;
1185 res = EVP_PKEY_decrypt(ctx, NULL, &outlen, Stream_Buffer(context->commandData), inlen);
1186 if (res < 0)
1187 {
1188 WLog_ERR(TAG, "Failed to decrypt data");
1189 goto decrypt_failed;
1190 }
1191
1192 /* Prepare output buffer */
1193 context->responseData = Stream_New(NULL, outlen);
1194
1195 if (!context->responseData)
1196 {
1197 WLog_ERR(TAG, "Failed to create decryption buffer");
1198 goto decrypt_failed;
1199 }
1200
1201 /* Decrypt */
1202 res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1203 Stream_Buffer(context->commandData), inlen);
1204
1205 if (res < 0)
1206 {
1207 WLog_ERR(TAG, "Failed to decrypt data");
1208 goto decrypt_failed;
1209 }
1210
1211 Stream_SetLength(context->responseData, outlen);
1212 }
1213 rc = TRUE;
1214
1215decrypt_failed:
1216 EVP_PKEY_CTX_free(ctx);
1217 EVP_PKEY_free(pkey);
1218 vgids_reset_context_command_data(context);
1219 if (!rc)
1220 vgids_reset_context_response(context);
1221 return rc;
1222}
1223
1224static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,
1225 DWORD* responseSize)
1226{
1227 BYTE cla = 0;
1228 BYTE p1 = 0;
1229 BYTE p2 = 0;
1230 BYTE lc = 0;
1231 DWORD resultDataSize = 0;
1232 const BYTE* resultData = NULL;
1233 UINT16 status = ISO_STATUS_SUCCESS;
1234
1235 /* Perform security operation */
1236 if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL))
1237 return FALSE;
1238
1239 if (lc == 0)
1240 {
1241 status = ISO_STATUS_WRONGLC;
1242 goto create_response;
1243 }
1244
1245 /* Is our default key referenced? */
1246 if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1247 {
1248 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1249 goto create_response;
1250 }
1251
1252 /* is the pin protecting the key verified? */
1253 if (!context->pinVerified)
1254 {
1255 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1256 goto create_response;
1257 }
1258
1259 /* Append the data to the context command buffer (PSO might chain command data) */
1260 if (!context->commandData)
1261 {
1262 context->commandData = Stream_New(NULL, lc);
1263 if (!context->commandData)
1264 return FALSE;
1265 }
1266 else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1267 return FALSE;
1268
1269 Stream_Write(context->commandData, Stream_Pointer(s), lc);
1270 Stream_SealLength(context->commandData);
1271
1272 /* Check if the correct operation is requested for our current SE */
1273 switch (context->currentSE.crt)
1274 {
1275 case VGIDS_SE_CRT_SIGN:
1276 {
1277 if (p1 != 0x9E || p2 != 0x9A)
1278 {
1279 status = ISO_STATUS_INVALIDP1P2;
1280 break;
1281 }
1282
1283 /* If chaining is over perform op */
1284 if (!(cla & 0x10))
1285 vgids_perform_digital_signature(context);
1286 break;
1287 }
1288 case VGIDS_SE_CRT_CONF:
1289 {
1290 if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1291 {
1292 status = ISO_STATUS_INVALIDP1P2;
1293 break;
1294 }
1295
1296 /* If chaining is over perform op */
1297 if (!(cla & 0x10))
1298 vgids_perform_decrypt(context);
1299 break;
1300 }
1301 default:
1302 status = ISO_STATUS_INVALIDP1P2;
1303 break;
1304 }
1305
1306 /* Do chaining of response data if necessary */
1307 if (status == ISO_STATUS_SUCCESS && context->responseData)
1308 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1309
1310 /* Check APDU params */
1311create_response:
1312 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1313}
1314
1315static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,
1316 DWORD* responseSize)
1317{
1318 BYTE p1 = 0;
1319 BYTE p2 = 0;
1320 BYTE le = 0;
1321 DWORD resultDataSize = 0;
1322 const BYTE* resultData = NULL;
1323 DWORD expectedLen = 0;
1324 DWORD remainingSize = 0;
1325 UINT16 status = ISO_STATUS_SUCCESS;
1326
1327 /* Get response continues data transfer after a previous get data command */
1328 /* Check if there is any data to transfer left */
1329 if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1330 {
1331 status = ISO_STATUS_COMMANDNOTALLOWED;
1332 goto create_response;
1333 }
1334
1335 if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le))
1336 return FALSE;
1337
1338 /* Check APDU params */
1339 if (p1 != 00 || p2 != 0x00)
1340 {
1341 status = ISO_STATUS_INVALIDP1P2;
1342 goto create_response;
1343 }
1344
1345 /* LE = 0 means 256 bytes expected */
1346 expectedLen = le;
1347 if (expectedLen == 0)
1348 expectedLen = 256;
1349
1350 /* prepare response size and update offset */
1351 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1352 if (remainingSize < expectedLen)
1353 expectedLen = remainingSize;
1354
1355 resultData = Stream_Pointer(context->responseData);
1356 resultDataSize = expectedLen;
1357 Stream_Seek(context->responseData, expectedLen);
1358
1359 /* If more data is left return 61XX - otherwise 9000 */
1360 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1361 if (remainingSize > 0)
1362 {
1363 status = ISO_STATUS_MORE_DATA;
1364 if (remainingSize < 256)
1365 status |= (remainingSize & 0xFF);
1366 }
1367
1368create_response:
1369 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1370}
1371
1372static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,
1373 DWORD* responseSize)
1374{
1375 BYTE ins = 0;
1376 BYTE p1 = 0;
1377 BYTE p2 = 0;
1378 BYTE lc = 0;
1379 UINT16 status = ISO_STATUS_SUCCESS;
1380 char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 };
1381
1382 /* Verify is always called for the application password (PIN) P2=0x80 */
1383 if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL))
1384 return FALSE;
1385
1386 /* Check APDU params */
1387 if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1388 {
1389 status = ISO_STATUS_INVALIDP1P2;
1390 goto create_response;
1391 }
1392
1393 /* shall we reset the security state? */
1394 if (p2 == 0x82)
1395 {
1396 context->pinVerified = FALSE;
1397 goto create_response;
1398 }
1399
1400 /* Check if pin is not already blocked */
1401 if (context->curRetryCounter == 0)
1402 {
1403 status = ISO_STATUS_AUTHMETHODBLOCKED;
1404 goto create_response;
1405 }
1406
1407 /* Read and verify LC */
1408 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1409 {
1410 status = ISO_STATUS_INVALIDLC;
1411 goto create_response;
1412 }
1413
1414 Stream_Read_UINT8(s, lc);
1415 if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1416 {
1417 status = ISO_STATUS_INVALIDLC;
1418 goto create_response;
1419 }
1420
1421 /* read and verify pin */
1422 Stream_Read(s, pin, lc);
1423 if (strcmp(context->pin, pin) != 0)
1424 {
1425 /* retries are encoded in the lowest 4-bit of the status code */
1426 --context->curRetryCounter;
1427 context->pinVerified = FALSE;
1428 status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
1429 }
1430 else
1431 {
1432 /* reset retry counter and mark pin as verified */
1433 context->curRetryCounter = context->retryCounter;
1434 context->pinVerified = TRUE;
1435 }
1436
1437create_response:
1438 return vgids_create_response(status, NULL, 0, response, responseSize);
1439}
1440
1441vgidsContext* vgids_new(void)
1442{
1443 wObject* obj = NULL;
1444 vgidsContext* ctx = calloc(1, sizeof(vgidsContext));
1445
1446 ctx->files = ArrayList_New(FALSE);
1447 if (!ctx->files)
1448 {
1449 WLog_ERR(TAG, "Failed to create files array list");
1450 goto create_failed;
1451 }
1452
1453 obj = ArrayList_Object(ctx->files);
1454 obj->fnObjectFree = vgids_ef_free;
1455
1456 return ctx;
1457
1458create_failed:
1459 vgids_free(ctx);
1460 return NULL;
1461}
1462
1463BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)
1464{
1465 DWORD kxcSize = 0;
1466 DWORD keymapSize = 0;
1467 DWORD fsTableSize = 0;
1468 BOOL rc = FALSE;
1469 BYTE* kxc = NULL;
1470 BYTE* keymap = NULL;
1471 BYTE* fsTable = NULL;
1472 vgidsEF* masterEF = NULL;
1473 vgidsEF* cardidEF = NULL;
1474 vgidsEF* commonEF = NULL;
1475 BYTE cardid[VGIDS_CARDID_SIZE] = { 0 };
1476 vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ',
1477 '0', '0' },
1478 CONTAINER_MAP_VALID_CONTAINER |
1479 CONTAINER_MAP_DEFAULT_CONTAINER,
1480 0,
1481 0,
1482 0x00 /* key-size in bits - filled out later */ };
1483 vgidsFilesysTableEntry filesys[] = {
1484 { "mscp", "", 0, 0, 0, 0xA000, 0 },
1485 { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 },
1486 { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1487 { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1488 { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1489 { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1490 };
1491
1492 /* Check params */
1493 if (!cert || !privateKey || !pin)
1494 {
1495 WLog_DBG(TAG, "Passed invalid NULL argument: cert=%p, privateKey=%p, pin=%p", cert,
1496 privateKey, pin);
1497 goto init_failed;
1498 }
1499
1500 /* Convert PEM input to DER certificate/public key/private key */
1501 ctx->certificate = freerdp_certificate_new_from_pem(cert);
1502 if (!ctx->certificate)
1503 goto init_failed;
1504
1505 ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey, NULL);
1506 if (!ctx->privateKey)
1507 goto init_failed;
1508
1509 /* create masterfile */
1510 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1511 masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1512 if (!masterEF)
1513 goto init_failed;
1514
1515 /* create cardid file with cardid DO */
1516 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1517 cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1518 if (!cardidEF)
1519 goto init_failed;
1520 winpr_RAND(cardid, sizeof(cardid));
1521 if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))
1522 goto init_failed;
1523
1524 /* create user common file */
1525 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1526 commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1527 if (!commonEF)
1528 goto init_failed;
1529
1530 /* write card cache DO */
1531 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))
1532 goto init_failed;
1533
1534 /* write container map DO */
1535 {
1536 const size_t size = get_rsa_key_size(ctx->privateKey);
1537 if ((size == 0) || (size > UINT16_MAX / 8))
1538 goto init_failed;
1539
1540 cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1541 }
1542 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))
1543 goto init_failed;
1544
1545 /* write cardapps DO */
1546 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1547 sizeof(g_CardAppsContents)))
1548 goto init_failed;
1549
1550 /* convert and write certificate to key exchange container */
1551 if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1552 goto init_failed;
1553 if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1554 goto init_failed;
1555
1556 /* prepare and write file system table */
1557 if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1558 goto init_failed;
1559 if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1560 goto init_failed;
1561
1562 /* vgids_prepare_keymap and write to masterEF */
1563 if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1564 goto init_failed;
1565 if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1566 goto init_failed;
1567
1568 /* store user pin */
1569 ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
1570 ctx->pin = _strdup(pin);
1571 if (!ctx->pin)
1572 goto init_failed;
1573
1574 rc = TRUE;
1575
1576init_failed:
1577 // ArrayList_Append in vgids_ef_new takes ownership
1578 // of cardidEF, commonEF, masterEF
1579 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1580 free(kxc);
1581 free(keymap);
1582 free(fsTable);
1583 return rc;
1584}
1585
1586BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,
1587 DWORD* responseSize)
1588{
1589 wStream s;
1590 static int x = 1;
1591
1592 /* Check params */
1593 if (!context || !data || !response || !responseSize)
1594 {
1595 WLog_ERR(TAG, "Invalid NULL pointer passed");
1596 return FALSE;
1597 }
1598
1599 if (dataSize < 4)
1600 {
1601 WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1602 return FALSE;
1603 }
1604
1605 /* Examine INS byte */
1606 Stream_StaticConstInit(&s, data, dataSize);
1607 if (x++ == 0xe)
1608 x = 0xe + 1;
1609 switch (data[1])
1610 {
1611 case ISO_INS_SELECT:
1612 return vgids_ins_select(context, &s, response, responseSize);
1613 case ISO_INS_GETDATA:
1614 return vgids_ins_getdata(context, &s, response, responseSize);
1615 case ISO_INS_GETRESPONSE:
1616 return vgids_ins_getresponse(context, &s, response, responseSize);
1617 case ISO_INS_MSE:
1618 return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1619 case ISO_INS_PSO:
1620 return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1621 case ISO_INS_VERIFY:
1622 return vgids_ins_verify(context, &s, response, responseSize);
1623 default:
1624 break;
1625 }
1626
1627 /* return command not allowed */
1628 return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize);
1629}
1630
1631void vgids_free(vgidsContext* context)
1632{
1633 if (context)
1634 {
1635 freerdp_key_free(context->privateKey);
1636 freerdp_certificate_free(context->certificate);
1637 Stream_Free(context->commandData, TRUE);
1638 Stream_Free(context->responseData, TRUE);
1639 free(context->pin);
1640 ArrayList_Free(context->files);
1641 free(context);
1642 }
1643}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57