Coverage Report

Created: 2026-03-04 06:17

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