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