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 if (!context->responseData)
660 free(response);
661 return FALSE;
662 }
663 }
664
665 return TRUE;
666}
667
668static BOOL vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
669{
670 return ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);
671}
672
673static void vgids_reset_context_response(vgidsContext* context)
674{
675 Stream_Free(context->responseData, TRUE);
676 context->responseData = nullptr;
677}
678
679static void vgids_reset_context_command_data(vgidsContext* context)
680{
681 Stream_Free(context->commandData, TRUE);
682 context->commandData = nullptr;
683}
684
685static BOOL vgids_ins_select(vgidsContext* context, wStream* s, BYTE** response,
686 DWORD* responseSize)
687{
688 BYTE p1 = 0;
689 BYTE p2 = 0;
690 BYTE lc = 0;
691 DWORD resultDataSize = 0;
692 const BYTE* resultData = nullptr;
693 UINT16 status = ISO_STATUS_SUCCESS;
694
695 /* The only select operations performed are either select by AID or select 3FFF (return
696 * information about the currently selected DF) */
697 if (!vgids_parse_apdu_header(s, nullptr, nullptr, &p1, &p2, &lc, nullptr))
698 return FALSE;
699
700 /* Check P1 for selection mode */
701 switch (p1)
702 {
703 /* Select by AID */
704 case 0x04:
705 {
706 /* read AID from APDU */
707 BYTE aid[ISO_AID_MAX_SIZE] = WINPR_C_ARRAY_INIT;
708 if (lc > ISO_AID_MAX_SIZE)
709 {
710 WLog_ERR(TAG, "The LC byte is greater than the maximum AID length");
711 status = ISO_STATUS_INVALIDLC;
712 break;
713 }
714
715 /* Check if we select MS GIDS App (only one we know) */
716 Stream_Read(s, aid, lc);
717 if (memcmp(aid, g_MsGidsAID, lc) != 0)
718 {
719 status = ISO_STATUS_FILENOTFOUND;
720 break;
721 }
722
723 /* Return FCI or FCP for MsGids App */
724 switch (p2)
725 {
726 /* Return FCI information */
727 case 0x00:
728 {
729 resultData = g_GidsAppFCI;
730 resultDataSize = sizeof(g_GidsAppFCI);
731 break;
732 }
733 /* Return FCP information */
734 case 0x04:
735 {
736 resultData = g_GidsAppFCP;
737 resultDataSize = sizeof(g_GidsAppFCP);
738 break;
739 }
740 default:
741 status = ISO_STATUS_INVALIDP1P2;
742 break;
743 }
744
745 if (resultData)
746 context->currentDF = ISO_FID_MF;
747 break;
748 }
749 /* Select by FID */
750 case 0x00:
751 {
752 /* read FID from APDU */
753 UINT16 fid = 0;
754 if (lc > 2)
755 {
756 WLog_ERR(TAG, "The LC byte for the file ID is greater than 2");
757 status = ISO_STATUS_INVALIDLC;
758 break;
759 }
760
761 Stream_Read_UINT16_BE(s, fid);
762 if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)
763 {
764 status = ISO_STATUS_FILENOTFOUND;
765 break;
766 }
767 break;
768 }
769 default:
770 {
771 /* P1 P2 combination not supported */
772 status = ISO_STATUS_INVALIDP1P2;
773 break;
774 }
775 }
776
777 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
778}
779
780static UINT16 vgids_handle_chained_response(vgidsContext* context, const BYTE** response,
781 DWORD* responseSize)
782{
783 /* Cap to a maximum of 256 bytes and set status to more data */
784 UINT16 status = ISO_STATUS_SUCCESS;
785 DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);
786 if (remainingBytes > 256)
787 {
788 status = ISO_STATUS_MORE_DATA;
789 remainingBytes = 256;
790 }
791
792 *response = Stream_Buffer(context->responseData);
793 *responseSize = remainingBytes;
794 Stream_Seek(context->responseData, remainingBytes);
795
796 /* Check if there are more than 256 bytes left or if we can already provide the remaining length
797 * in the status word */
798 remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);
799 if (remainingBytes < 256 && remainingBytes != 0)
800 status |= (remainingBytes & 0xFF);
801 return status;
802}
803
804static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)
805{
806 BOOL rc = FALSE;
807 wStream* pubKey = nullptr;
808 wStream* response = nullptr;
809
810 WINPR_ASSERT(context);
811
812 /* Get key components */
813 size_t nSize = 0;
814 size_t eSize = 0;
815
816 char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);
817 char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);
818
819 if (!n || !e)
820 goto handle_error;
821
822 pubKey = Stream_New(nullptr, nSize + eSize + 0x10);
823 if (!pubKey)
824 {
825 WLog_ERR(TAG, "Failed to allocate public key stream");
826 goto handle_error;
827 }
828
829 response = Stream_New(nullptr, Stream_Capacity(pubKey) + 0x10);
830 if (!response)
831 {
832 WLog_ERR(TAG, "Failed to allocate response stream");
833 goto handle_error;
834 }
835
836 /* write modulus and exponent DOs */
837 if (!vgids_write_tlv(pubKey, 0x81, n, nSize))
838 goto handle_error;
839
840 if (!vgids_write_tlv(pubKey, 0x82, e, eSize))
841 goto handle_error;
842
843 /* write ISO public key template */
844 if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))
845 goto handle_error;
846
847 /* set response data */
848 Stream_ResetPosition(response);
849 context->responseData = response;
850 response = nullptr;
851
852 rc = TRUE;
853handle_error:
854 free(n);
855 free(e);
856 Stream_Free(pubKey, TRUE);
857 Stream_Free(response, TRUE);
858 return rc;
859}
860
861static BOOL vgids_ins_getdata(vgidsContext* context, wStream* s, BYTE** response,
862 DWORD* responseSize)
863{
864 UINT16 doId = 0;
865 UINT16 fileId = 0;
866 BYTE p1 = 0;
867 BYTE p2 = 0;
868 BYTE lc = 0;
869 DWORD resultDataSize = 0;
870 const BYTE* resultData = nullptr;
871 UINT16 status = ISO_STATUS_SUCCESS;
872
873 /* GetData is called a lot!
874 - To retrieve DOs from files
875 - To retrieve public key information
876 */
877 if (!vgids_parse_apdu_header(s, nullptr, nullptr, &p1, &p2, &lc, nullptr))
878 return FALSE;
879
880 /* free any previous queried data */
881 vgids_reset_context_response(context);
882
883 /* build up file identifier */
884 fileId = (UINT16)(((UINT16)p1 << 8) | p2);
885
886 /* Do we have a DO reference? */
887 switch (lc)
888 {
889 case 4:
890 {
891 BYTE tag = 0;
892 BYTE length = 0;
893 Stream_Read_UINT8(s, tag);
894 Stream_Read_UINT8(s, length);
895 if (tag != 0x5C && length != 0x02)
896 {
897 status = ISO_STATUS_INVALIDCOMMANDDATA;
898 break;
899 }
900
901 Stream_Read_UINT16_BE(s, doId);
902
903 /* the function only returns if the ID was found in the list and iteration aborted early
904 * or not. we can ignore this here. */
905 (void)vgids_read_do(context, fileId, doId);
906 break;
907 }
908 case 0xA:
909 {
910 UINT16 pubKeyDO = 0;
911 BYTE tag = 0;
912 BYTE length = 0;
913 BYTE keyRef = 0;
914
915 /* We want to retrieve the public key? */
916 if (p1 != 0x3F && p2 != 0xFF)
917 {
918 status = ISO_STATUS_INVALIDP1P2;
919 break;
920 }
921
922 /* read parent tag/length */
923 Stream_Read_UINT8(s, tag);
924 Stream_Read_UINT8(s, length);
925 if (tag != 0x70 || length != 0x08)
926 {
927 status = ISO_STATUS_INVALIDCOMMANDDATA;
928 break;
929 }
930
931 /* read key reference TLV */
932 Stream_Read_UINT8(s, tag);
933 Stream_Read_UINT8(s, length);
934 Stream_Read_UINT8(s, keyRef);
935 if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
936 {
937 status = ISO_STATUS_INVALIDCOMMANDDATA;
938 break;
939 }
940
941 /* read key value template TLV */
942 Stream_Read_UINT8(s, tag);
943 Stream_Read_UINT8(s, length);
944 if (tag != 0xA5 || length != 0x03)
945 {
946 status = ISO_STATUS_INVALIDCOMMANDDATA;
947 break;
948 }
949
950 Stream_Read_UINT16_BE(s, pubKeyDO);
951 Stream_Read_UINT8(s, length);
952 if (pubKeyDO != 0x7F49 || length != 0x80)
953 {
954 status = ISO_STATUS_INVALIDCOMMANDDATA;
955 break;
956 }
957
958 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
959 {
960 status = ISO_STATUS_INVALIDLC;
961 break;
962 }
963
964 /* Return public key value */
965 vgids_get_public_key(context, pubKeyDO);
966 break;
967 }
968 default:
969 status = ISO_STATUS_INVALIDCOMMANDDATA;
970 break;
971 }
972
973 /* If we have response data, make it ready for return */
974 if (context->responseData)
975 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
976 else if (status == ISO_STATUS_SUCCESS)
977 status = ISO_STATUS_REFERENCEDATANOTFOUND;
978
979 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
980}
981
982static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s,
983 BYTE** response, DWORD* responseSize)
984{
985 BYTE tag = 0;
986 BYTE length = 0;
987 BYTE p1 = 0;
988 BYTE p2 = 0;
989 BYTE lc = 0;
990 DWORD resultDataSize = 0;
991 const BYTE* resultData = nullptr;
992 UINT16 status = ISO_STATUS_SUCCESS;
993
994 vgids_reset_context_command_data(context);
995 vgids_reset_context_response(context);
996
997 /* Manage security environment prepares the card for performing crypto operations. */
998 if (!vgids_parse_apdu_header(s, nullptr, nullptr, &p1, &p2, &lc, nullptr))
999 return FALSE;
1000
1001 /* Check APDU params */
1002 /* P1: Set Computation, decipherment, Internal Auth */
1003 /* P2: Digital Signature (B6), Confidentiality (B8) */
1004 if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
1005 {
1006 status = ISO_STATUS_INVALIDP1P2;
1007 goto create_response;
1008 }
1009
1010 if (lc != 6)
1011 {
1012 status = ISO_STATUS_WRONGLC;
1013 goto create_response;
1014 }
1015
1016 context->currentSE.crt = p2;
1017
1018 /* parse command buffer */
1019 /* Read algo ID */
1020 Stream_Read_UINT8(s, tag);
1021 Stream_Read_UINT8(s, length);
1022 if (tag != 0x80 || length != 0x01)
1023 {
1024 status = ISO_STATUS_INVALIDCOMMANDDATA;
1025 goto create_response;
1026 }
1027 Stream_Read_UINT8(s, context->currentSE.algoId);
1028
1029 /* Read private key reference */
1030 Stream_Read_UINT8(s, tag);
1031 Stream_Read_UINT8(s, length);
1032 if (tag != 0x84 || length != 0x01)
1033 {
1034 status = ISO_STATUS_INVALIDCOMMANDDATA;
1035 goto create_response;
1036 }
1037 Stream_Read_UINT8(s, context->currentSE.keyRef);
1038
1039create_response:
1040 /* If an error occurred reset SE */
1041 if (status != ISO_STATUS_SUCCESS)
1042 memset(&context->currentSE, 0, sizeof(context->currentSE));
1043 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1044}
1045
1046static BOOL vgids_perform_digital_signature(vgidsContext* context)
1047{
1048 size_t sigSize = 0;
1049 size_t msgSize = 0;
1050 EVP_PKEY_CTX* ctx = nullptr;
1051 EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
1052 const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
1053 { g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() },
1054 { g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() },
1055 { g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() },
1056 { g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() },
1057 { g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() },
1058#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1059 { g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
1060 { g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
1061#endif
1062 };
1063
1064 if (!pk)
1065 {
1066 WLog_ERR(TAG, "Failed to create PKEY");
1067 return FALSE;
1068 }
1069
1070 vgids_reset_context_response(context);
1071
1072 /* for each digest info */
1073 Stream_ResetPosition(context->commandData);
1074 for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
1075 {
1076 /* have we found our digest? */
1077 const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
1078 if (Stream_Length(context->commandData) >= digest->infoSize &&
1079 memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
1080 {
1081 /* skip digest info and calculate message size */
1082 Stream_Seek(context->commandData, digest->infoSize);
1083 if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
1084 goto sign_failed;
1085 msgSize = Stream_GetRemainingLength(context->commandData);
1086
1087 /* setup signing context */
1088 ctx = EVP_PKEY_CTX_new(pk, nullptr);
1089 if (!ctx)
1090 {
1091 WLog_ERR(TAG, "Failed to create signing context");
1092 goto sign_failed;
1093 }
1094
1095 if (EVP_PKEY_sign_init(ctx) <= 0)
1096 {
1097 WLog_ERR(TAG, "Failed to init signing context");
1098 goto sign_failed;
1099 }
1100
1101 /* set padding and signature algo */
1102 if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
1103 {
1104 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
1105 {
1106 WLog_ERR(TAG, "Failed to set padding mode");
1107 goto sign_failed;
1108 }
1109 }
1110
1111 if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
1112 {
1113 WLog_ERR(TAG, "Failed to set signing mode");
1114 goto sign_failed;
1115 }
1116
1117 /* Determine buffer length */
1118 if (EVP_PKEY_sign(ctx, nullptr, &sigSize, Stream_Pointer(context->commandData),
1119 msgSize) <= 0)
1120 {
1121 WLog_ERR(TAG, "Failed to determine signature size");
1122 goto sign_failed;
1123 }
1124
1125 context->responseData = Stream_New(nullptr, sigSize);
1126 if (!context->responseData)
1127 {
1128 WLog_ERR(TAG, "Failed to allocate signing buffer");
1129 goto sign_failed;
1130 }
1131
1132 /* sign */
1133 if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
1134 Stream_Pointer(context->commandData), msgSize) <= 0)
1135 {
1136 WLog_ERR(TAG, "Failed to create signature");
1137 goto sign_failed;
1138 }
1139
1140 if (!Stream_SetLength(context->responseData, sigSize))
1141 goto sign_failed;
1142
1143 EVP_PKEY_CTX_free(ctx);
1144 break;
1145 }
1146 }
1147
1148 EVP_PKEY_free(pk);
1149 vgids_reset_context_command_data(context);
1150 return TRUE;
1151
1152sign_failed:
1153 vgids_reset_context_command_data(context);
1154 vgids_reset_context_response(context);
1155 EVP_PKEY_CTX_free(ctx);
1156 EVP_PKEY_free(pk);
1157 return FALSE;
1158}
1159
1160static BOOL vgids_perform_decrypt(vgidsContext* context)
1161{
1162 EVP_PKEY_CTX* ctx = nullptr;
1163 BOOL rc = FALSE;
1164 int res = 0;
1165 int padding = RSA_NO_PADDING;
1166
1167 vgids_reset_context_response(context);
1168
1169 /* determine padding */
1170 if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
1171 padding = RSA_PKCS1_PADDING;
1172 else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
1173 padding = RSA_PKCS1_OAEP_PADDING;
1174
1175 /* init response buffer */
1176 EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
1177 if (!pkey)
1178 goto decrypt_failed;
1179 ctx = EVP_PKEY_CTX_new(pkey, nullptr);
1180 if (!ctx)
1181 goto decrypt_failed;
1182 if (EVP_PKEY_decrypt_init(ctx) <= 0)
1183 goto decrypt_failed;
1184 if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
1185 goto decrypt_failed;
1186
1187 /* Determine buffer length */
1188 {
1189 const size_t inlen = Stream_Length(context->commandData);
1190 size_t outlen = 0;
1191 res = EVP_PKEY_decrypt(ctx, nullptr, &outlen, Stream_Buffer(context->commandData), inlen);
1192 if (res < 0)
1193 {
1194 WLog_ERR(TAG, "Failed to decrypt data");
1195 goto decrypt_failed;
1196 }
1197
1198 /* Prepare output buffer */
1199 context->responseData = Stream_New(nullptr, outlen);
1200
1201 if (!context->responseData)
1202 {
1203 WLog_ERR(TAG, "Failed to create decryption buffer");
1204 goto decrypt_failed;
1205 }
1206
1207 /* Decrypt */
1208 res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1209 Stream_Buffer(context->commandData), inlen);
1210
1211 if (res < 0)
1212 {
1213 WLog_ERR(TAG, "Failed to decrypt data");
1214 goto decrypt_failed;
1215 }
1216
1217 rc = Stream_SetLength(context->responseData, outlen);
1218 }
1219
1220decrypt_failed:
1221 EVP_PKEY_CTX_free(ctx);
1222 EVP_PKEY_free(pkey);
1223 vgids_reset_context_command_data(context);
1224 if (!rc)
1225 vgids_reset_context_response(context);
1226 return rc;
1227}
1228
1229static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,
1230 DWORD* responseSize)
1231{
1232 BYTE cla = 0;
1233 BYTE p1 = 0;
1234 BYTE p2 = 0;
1235 BYTE lc = 0;
1236 DWORD resultDataSize = 0;
1237 const BYTE* resultData = nullptr;
1238 UINT16 status = ISO_STATUS_SUCCESS;
1239
1240 /* Perform security operation */
1241 if (!vgids_parse_apdu_header(s, &cla, nullptr, &p1, &p2, &lc, nullptr))
1242 return FALSE;
1243
1244 if (lc == 0)
1245 {
1246 status = ISO_STATUS_WRONGLC;
1247 goto create_response;
1248 }
1249
1250 /* Is our default key referenced? */
1251 if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1252 {
1253 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1254 goto create_response;
1255 }
1256
1257 /* is the pin protecting the key verified? */
1258 if (!context->pinVerified)
1259 {
1260 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1261 goto create_response;
1262 }
1263
1264 /* Append the data to the context command buffer (PSO might chain command data) */
1265 if (!context->commandData)
1266 {
1267 context->commandData = Stream_New(nullptr, lc);
1268 if (!context->commandData)
1269 return FALSE;
1270 }
1271 else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1272 return FALSE;
1273
1274 Stream_Write(context->commandData, Stream_Pointer(s), lc);
1275 Stream_SealLength(context->commandData);
1276
1277 /* Check if the correct operation is requested for our current SE */
1278 switch (context->currentSE.crt)
1279 {
1280 case VGIDS_SE_CRT_SIGN:
1281 {
1282 if (p1 != 0x9E || p2 != 0x9A)
1283 {
1284 status = ISO_STATUS_INVALIDP1P2;
1285 break;
1286 }
1287
1288 /* If chaining is over perform op */
1289 if (!(cla & 0x10))
1290 vgids_perform_digital_signature(context);
1291 break;
1292 }
1293 case VGIDS_SE_CRT_CONF:
1294 {
1295 if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1296 {
1297 status = ISO_STATUS_INVALIDP1P2;
1298 break;
1299 }
1300
1301 /* If chaining is over perform op */
1302 if (!(cla & 0x10))
1303 vgids_perform_decrypt(context);
1304 break;
1305 }
1306 default:
1307 status = ISO_STATUS_INVALIDP1P2;
1308 break;
1309 }
1310
1311 /* Do chaining of response data if necessary */
1312 if (status == ISO_STATUS_SUCCESS && context->responseData)
1313 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1314
1315 /* Check APDU params */
1316create_response:
1317 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1318}
1319
1320static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,
1321 DWORD* responseSize)
1322{
1323 BYTE p1 = 0;
1324 BYTE p2 = 0;
1325 BYTE le = 0;
1326 DWORD resultDataSize = 0;
1327 const BYTE* resultData = nullptr;
1328 DWORD expectedLen = 0;
1329 DWORD remainingSize = 0;
1330 UINT16 status = ISO_STATUS_SUCCESS;
1331
1332 /* Get response continues data transfer after a previous get data command */
1333 /* Check if there is any data to transfer left */
1334 if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1335 {
1336 status = ISO_STATUS_COMMANDNOTALLOWED;
1337 goto create_response;
1338 }
1339
1340 if (!vgids_parse_apdu_header(s, nullptr, nullptr, &p1, &p2, nullptr, &le))
1341 return FALSE;
1342
1343 /* Check APDU params */
1344 if (p1 != 00 || p2 != 0x00)
1345 {
1346 status = ISO_STATUS_INVALIDP1P2;
1347 goto create_response;
1348 }
1349
1350 /* LE = 0 means 256 bytes expected */
1351 expectedLen = le;
1352 if (expectedLen == 0)
1353 expectedLen = 256;
1354
1355 /* prepare response size and update offset */
1356 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1357 if (remainingSize < expectedLen)
1358 expectedLen = remainingSize;
1359
1360 resultData = Stream_Pointer(context->responseData);
1361 resultDataSize = expectedLen;
1362 Stream_Seek(context->responseData, expectedLen);
1363
1364 /* If more data is left return 61XX - otherwise 9000 */
1365 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1366 if (remainingSize > 0)
1367 {
1368 status = ISO_STATUS_MORE_DATA;
1369 if (remainingSize < 256)
1370 status |= (remainingSize & 0xFF);
1371 }
1372
1373create_response:
1374 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1375}
1376
1377static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,
1378 DWORD* responseSize)
1379{
1380 BYTE ins = 0;
1381 BYTE p1 = 0;
1382 BYTE p2 = 0;
1383 BYTE lc = 0;
1384 UINT16 status = ISO_STATUS_SUCCESS;
1385 char pin[VGIDS_MAX_PIN_SIZE + 1] = WINPR_C_ARRAY_INIT;
1386
1387 /* Verify is always called for the application password (PIN) P2=0x80 */
1388 if (!vgids_parse_apdu_header(s, nullptr, &ins, &p1, &p2, nullptr, nullptr))
1389 return FALSE;
1390
1391 /* Check APDU params */
1392 if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1393 {
1394 status = ISO_STATUS_INVALIDP1P2;
1395 goto create_response;
1396 }
1397
1398 /* shall we reset the security state? */
1399 if (p2 == 0x82)
1400 {
1401 context->pinVerified = FALSE;
1402 goto create_response;
1403 }
1404
1405 /* Check if pin is not already blocked */
1406 if (context->curRetryCounter == 0)
1407 {
1408 status = ISO_STATUS_AUTHMETHODBLOCKED;
1409 goto create_response;
1410 }
1411
1412 /* Read and verify LC */
1413 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1414 {
1415 status = ISO_STATUS_INVALIDLC;
1416 goto create_response;
1417 }
1418
1419 Stream_Read_UINT8(s, lc);
1420 if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1421 {
1422 status = ISO_STATUS_INVALIDLC;
1423 goto create_response;
1424 }
1425
1426 /* read and verify pin */
1427 Stream_Read(s, pin, lc);
1428 if (strcmp(context->pin, pin) != 0)
1429 {
1430 /* retries are encoded in the lowest 4-bit of the status code */
1431 --context->curRetryCounter;
1432 context->pinVerified = FALSE;
1433 status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
1434 }
1435 else
1436 {
1437 /* reset retry counter and mark pin as verified */
1438 context->curRetryCounter = context->retryCounter;
1439 context->pinVerified = TRUE;
1440 }
1441
1442create_response:
1443 return vgids_create_response(status, nullptr, 0, response, responseSize);
1444}
1445
1446vgidsContext* vgids_new(void)
1447{
1448 wObject* obj = nullptr;
1449 vgidsContext* ctx = calloc(1, sizeof(vgidsContext));
1450
1451 ctx->files = ArrayList_New(FALSE);
1452 if (!ctx->files)
1453 {
1454 WLog_ERR(TAG, "Failed to create files array list");
1455 goto create_failed;
1456 }
1457
1458 obj = ArrayList_Object(ctx->files);
1459 obj->fnObjectFree = vgids_ef_free;
1460
1461 return ctx;
1462
1463create_failed:
1464 vgids_free(ctx);
1465 return nullptr;
1466}
1467
1468BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)
1469{
1470 DWORD kxcSize = 0;
1471 DWORD keymapSize = 0;
1472 DWORD fsTableSize = 0;
1473 BOOL rc = FALSE;
1474 BYTE* kxc = nullptr;
1475 BYTE* keymap = nullptr;
1476 BYTE* fsTable = nullptr;
1477 vgidsEF* masterEF = nullptr;
1478 vgidsEF* cardidEF = nullptr;
1479 vgidsEF* commonEF = nullptr;
1480 BYTE cardid[VGIDS_CARDID_SIZE] = WINPR_C_ARRAY_INIT;
1481 vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ',
1482 '0', '0' },
1483 CONTAINER_MAP_VALID_CONTAINER |
1484 CONTAINER_MAP_DEFAULT_CONTAINER,
1485 0,
1486 0,
1487 0x00 /* key-size in bits - filled out later */ };
1488 vgidsFilesysTableEntry filesys[] = {
1489 { "mscp", "", 0, 0, 0, 0xA000, 0 },
1490 { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 },
1491 { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1492 { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1493 { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1494 { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1495 };
1496
1497 /* Check params */
1498 if (!cert || !privateKey || !pin)
1499 {
1500 WLog_DBG(TAG, "Passed invalid nullptr argument: cert=%p, privateKey=%p, pin=%p",
1501 WINPR_CXX_COMPAT_CAST(const void*, cert),
1502 WINPR_CXX_COMPAT_CAST(const void*, privateKey),
1503 WINPR_CXX_COMPAT_CAST(const void*, pin));
1504 goto init_failed;
1505 }
1506
1507 /* Convert PEM input to DER certificate/public key/private key */
1508 ctx->certificate = freerdp_certificate_new_from_pem(cert);
1509 if (!ctx->certificate)
1510 goto init_failed;
1511
1512 ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey, nullptr);
1513 if (!ctx->privateKey)
1514 goto init_failed;
1515
1516 /* create masterfile */
1517 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1518 masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1519 if (!masterEF)
1520 goto init_failed;
1521
1522 /* create cardid file with cardid DO */
1523 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1524 cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1525 if (!cardidEF)
1526 goto init_failed;
1527 if (winpr_RAND(cardid, sizeof(cardid)) < 0)
1528 goto init_failed;
1529 if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))
1530 goto init_failed;
1531
1532 /* create user common file */
1533 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1534 commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1535 if (!commonEF)
1536 goto init_failed;
1537
1538 /* write card cache DO */
1539 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))
1540 goto init_failed;
1541
1542 /* write container map DO */
1543 {
1544 const size_t size = get_rsa_key_size(ctx->privateKey);
1545 if ((size == 0) || (size > UINT16_MAX / 8))
1546 goto init_failed;
1547
1548 cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1549 }
1550 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))
1551 goto init_failed;
1552
1553 /* write cardapps DO */
1554 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1555 sizeof(g_CardAppsContents)))
1556 goto init_failed;
1557
1558 /* convert and write certificate to key exchange container */
1559 if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1560 goto init_failed;
1561 if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1562 goto init_failed;
1563
1564 /* prepare and write file system table */
1565 if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1566 goto init_failed;
1567 if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1568 goto init_failed;
1569
1570 /* vgids_prepare_keymap and write to masterEF */
1571 if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1572 goto init_failed;
1573 if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1574 goto init_failed;
1575
1576 /* store user pin */
1577 ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
1578 ctx->pin = _strdup(pin);
1579 if (!ctx->pin)
1580 goto init_failed;
1581
1582 rc = TRUE;
1583
1584init_failed:
1585 // ArrayList_Append in vgids_ef_new takes ownership
1586 // of cardidEF, commonEF, masterEF
1587 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1588 free(kxc);
1589 free(keymap);
1590 free(fsTable);
1591 return rc;
1592}
1593
1594BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,
1595 DWORD* responseSize)
1596{
1597 wStream s;
1598 static int x = 1;
1599
1600 /* Check params */
1601 if (!context || !data || !response || !responseSize)
1602 {
1603 WLog_ERR(TAG, "Invalid nullptr pointer passed");
1604 return FALSE;
1605 }
1606
1607 if (dataSize < 4)
1608 {
1609 WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1610 return FALSE;
1611 }
1612
1613 /* Examine INS byte */
1614 Stream_StaticConstInit(&s, data, dataSize);
1615 if (x++ == 0xe)
1616 x = 0xe + 1;
1617 switch (data[1])
1618 {
1619 case ISO_INS_SELECT:
1620 return vgids_ins_select(context, &s, response, responseSize);
1621 case ISO_INS_GETDATA:
1622 return vgids_ins_getdata(context, &s, response, responseSize);
1623 case ISO_INS_GETRESPONSE:
1624 return vgids_ins_getresponse(context, &s, response, responseSize);
1625 case ISO_INS_MSE:
1626 return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1627 case ISO_INS_PSO:
1628 return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1629 case ISO_INS_VERIFY:
1630 return vgids_ins_verify(context, &s, response, responseSize);
1631 default:
1632 break;
1633 }
1634
1635 /* return command not allowed */
1636 return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, nullptr, 0, response, responseSize);
1637}
1638
1639void vgids_free(vgidsContext* context)
1640{
1641 if (context)
1642 {
1643 freerdp_key_free(context->privateKey);
1644 freerdp_certificate_free(context->certificate);
1645 Stream_Free(context->commandData, TRUE);
1646 Stream_Free(context->responseData, TRUE);
1647 free(context->pin);
1648 ArrayList_Free(context->files);
1649 free(context);
1650 }
1651}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59