Coverage Report

Created: 2026-10-15 06:31

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