Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/emu/scard/smartcard_virtual_gids.c
Line
Count
Source (jump to first uncovered line)
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(NULL, 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
  Stream_SetLength(ef->data, 0);
283
284
0
  if (!ArrayList_Append(ctx->files, ef))
285
0
  {
286
0
    WLog_ERR(TAG, "Failed to add new ef to file list");
287
0
    goto create_failed;
288
0
  }
289
290
0
  return ef;
291
292
0
create_failed:
293
0
  vgids_ef_free(ef);
294
0
  return NULL;
295
0
}
296
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 + 5))
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
static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID, const void* data, DWORD dataSize)
334
0
{
335
  /* Write DO to end of file: 2-Byte ID, 1-Byte Len, Data */
336
0
  return vgids_write_tlv(ef->data, doID, data, dataSize);
337
0
}
338
339
static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)
340
0
{
341
  /* Read the given DO from the file: 2-Byte ID, 1-Byte Len, Data */
342
0
  if (!Stream_SetPosition(ef->data, 0))
343
0
  {
344
0
    WLog_ERR(TAG, "Failed to seek to front of file");
345
0
    return FALSE;
346
0
  }
347
348
  /* Look for the requested DO */
349
0
  while (Stream_GetRemainingLength(ef->data) > 3)
350
0
  {
351
0
    BYTE len = 0;
352
0
    size_t curPos = 0;
353
0
    UINT16 doSize = 0;
354
0
    UINT16 nextDOID = 0;
355
356
0
    curPos = Stream_GetPosition(ef->data);
357
0
    Stream_Read_UINT16_BE(ef->data, nextDOID);
358
0
    Stream_Read_UINT8(ef->data, len);
359
0
    if ((len & 0x80))
360
0
    {
361
0
      BYTE lenSize = len & 0x7F;
362
0
      if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))
363
0
        return FALSE;
364
365
0
      switch (lenSize)
366
0
      {
367
0
        case 1:
368
0
          Stream_Read_UINT8(ef->data, doSize);
369
0
          break;
370
0
        case 2:
371
0
          Stream_Read_UINT16_BE(ef->data, doSize);
372
0
          break;
373
0
        default:
374
0
          WLog_ERR(TAG, "Unexpected tag length %" PRIu8, lenSize);
375
0
          return FALSE;
376
0
      }
377
0
    }
378
0
    else
379
0
      doSize = len;
380
381
0
    if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))
382
0
      return FALSE;
383
384
0
    if (nextDOID == doID)
385
0
    {
386
0
      BYTE* outData = NULL;
387
388
      /* Include Tag and length in result */
389
0
      doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);
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_SetPosition(ef->data, curPos);
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
static BOOL vgids_prepare_fstable(const vgidsFilesysTableEntry* fstable, DWORD numEntries,
425
                                  BYTE** outData, DWORD* outDataSize)
426
0
{
427
  /* Filesystem table:
428
      BYTE unknown: 0x01
429
      Array of vgidsFilesysTableEntry
430
  */
431
0
  BYTE* data = malloc(sizeof(vgidsFilesysTableEntry) * numEntries + 1);
432
0
  if (!data)
433
0
  {
434
0
    WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
435
0
    return FALSE;
436
0
  }
437
438
0
  *data = 0x01;
439
0
  for (UINT32 i = 0; i < numEntries; ++i)
440
0
    memcpy(data + 1 + (sizeof(vgidsFilesysTableEntry) * i), &fstable[i],
441
0
           sizeof(vgidsFilesysTableEntry));
442
443
0
  *outData = data;
444
0
  *outDataSize = sizeof(vgidsFilesysTableEntry) * numEntries + 1;
445
446
0
  return TRUE;
447
0
}
448
449
static BOOL vgids_prepare_certificate(const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)
450
0
{
451
  /* Key exchange container:
452
      UINT16 compression version: 0001
453
      UINT16 source size
454
      ZLIB compressed cert
455
  */
456
0
  uLongf destSize = 0;
457
0
  wStream* s = NULL;
458
0
  BYTE* comprData = NULL;
459
460
0
  WINPR_ASSERT(cert);
461
462
0
  size_t certSize = 0;
463
0
  BYTE* certData = freerdp_certificate_get_der(cert, &certSize);
464
0
  if (!certData || (certSize == 0) || (certSize > UINT16_MAX))
465
0
  {
466
0
    WLog_ERR(TAG, "Failed to get certificate size");
467
0
    goto handle_error;
468
0
  }
469
470
0
  comprData = malloc(certSize);
471
0
  if (!comprData)
472
0
  {
473
0
    WLog_ERR(TAG, "Failed to allocate certificate buffer");
474
0
    goto handle_error;
475
0
  }
476
477
  /* compress certificate data */
478
0
  destSize = WINPR_ASSERTING_INT_CAST(uint16_t, certSize);
479
0
  if (compress(comprData, &destSize, certData, WINPR_ASSERTING_INT_CAST(uint16_t, certSize)) !=
480
0
      Z_OK)
481
0
  {
482
0
    WLog_ERR(TAG, "Failed to compress certificate data");
483
0
    goto handle_error;
484
0
  }
485
486
  /* Write container data */
487
0
  s = Stream_New(NULL, destSize + 4);
488
0
  Stream_Write_UINT16(s, 0x0001);
489
0
  Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, certSize));
490
0
  Stream_Write(s, comprData, destSize);
491
0
  Stream_SealLength(s);
492
493
0
  *kxc = Stream_Buffer(s);
494
0
  *kxcSize = (DWORD)Stream_Length(s);
495
496
0
  Stream_Free(s, FALSE);
497
0
  free(certData);
498
0
  free(comprData);
499
0
  return TRUE;
500
501
0
handle_error:
502
0
  Stream_Free(s, TRUE);
503
0
  free(certData);
504
0
  free(comprData);
505
0
  return FALSE;
506
0
}
507
508
static size_t get_rsa_key_size(const rdpPrivateKey* privateKey)
509
0
{
510
0
  WINPR_ASSERT(privateKey);
511
512
0
  return freerdp_key_get_bits(privateKey) / 8;
513
0
}
514
515
static BYTE vgids_get_algid(vgidsContext* p_Ctx)
516
0
{
517
0
  WINPR_ASSERT(p_Ctx);
518
519
0
  switch (get_rsa_key_size(p_Ctx->privateKey))
520
0
  {
521
0
    case (1024 / 8):
522
0
      return VGIDS_ALGID_RSA_1024;
523
0
    case (2048 / 8):
524
0
      return VGIDS_ALGID_RSA_2048;
525
0
    case (3072 / 8):
526
0
      return VGIDS_ALGID_RSA_3072;
527
0
    case (4096 / 8):
528
0
      return VGIDS_ALGID_RSA_4096;
529
0
    default:
530
0
      WLog_ERR(TAG, "Failed to determine algid for private key");
531
0
      break;
532
0
  }
533
534
0
  return 0;
535
0
}
536
537
static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)
538
0
{
539
  /* Key map record table:
540
      BYTE unknown (count?): 0x01
541
      Array of vgidsKeymapRecord
542
  */
543
0
  BYTE* data = NULL;
544
0
  vgidsKeymapRecord record = {
545
0
    1,                                /* state */
546
0
    0,                                /* algo */
547
0
    VGIDS_KEY_TYPE_KEYEXCHANGE,       /* keytpe */
548
0
    (0xB000 | VGIDS_DEFAULT_KEY_REF), /* keyref */
549
0
    0xFFFF,                           /* unknown FFFF */
550
0
    0x0000                            /* unknown 0000 */
551
0
  };
552
553
  /* Determine algo */
554
0
  BYTE algid = vgids_get_algid(context);
555
0
  if (algid == 0)
556
0
    return FALSE;
557
558
0
  data = malloc(sizeof(record) + 1);
559
0
  if (!data)
560
0
  {
561
0
    WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
562
0
    return FALSE;
563
0
  }
564
565
0
  *data = 0x01;
566
0
  record.algid = algid;
567
0
  memcpy(data + 1, &record, sizeof(record));
568
569
0
  *outData = data;
570
0
  *outDataSize = sizeof(record) + 1;
571
572
0
  return TRUE;
573
0
}
574
575
static BOOL vgids_parse_apdu_header(wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,
576
                                    BYTE* le)
577
0
{
578
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
579
0
    return FALSE;
580
581
  /* Read and verify APDU data */
582
0
  if (cla)
583
0
    Stream_Read_UINT8(s, *cla);
584
0
  else
585
0
    Stream_Seek(s, 1);
586
0
  if (ins)
587
0
    Stream_Read_UINT8(s, *ins);
588
0
  else
589
0
    Stream_Seek(s, 1);
590
0
  if (p1)
591
0
    Stream_Read_UINT8(s, *p1);
592
0
  else
593
0
    Stream_Seek(s, 1);
594
0
  if (p2)
595
0
    Stream_Read_UINT8(s, *p2);
596
0
  else
597
0
    Stream_Seek(s, 1);
598
599
  /* If LC is requested - check remaining length and read as well */
600
0
  if (lc)
601
0
  {
602
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
603
0
      return FALSE;
604
605
0
    Stream_Read_UINT8(s, *lc);
606
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))
607
0
      return FALSE;
608
0
  }
609
610
  /* read LE */
611
0
  if (le)
612
0
  {
613
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
614
0
      return FALSE;
615
0
    Stream_Read_UINT8(s, *le);
616
0
  }
617
618
0
  return TRUE;
619
0
}
620
621
static BOOL vgids_create_response(UINT16 status, const BYTE* answer, DWORD answerSize,
622
                                  BYTE** outData, DWORD* outDataSize)
623
0
{
624
0
  BYTE* out = malloc(answerSize + 2);
625
0
  if (!out)
626
0
  {
627
0
    WLog_ERR(TAG, "Failed to allocate memory for response data");
628
0
    return FALSE;
629
0
  }
630
631
0
  *outData = out;
632
0
  if (answer)
633
0
  {
634
0
    memcpy(out, answer, answerSize);
635
0
    out += answerSize;
636
0
  }
637
638
0
  *out = (BYTE)((status >> 8) & 0xFF);
639
0
  *(out + 1) = (BYTE)(status & 0xFF);
640
0
  *outDataSize = answerSize + 2;
641
0
  return TRUE;
642
0
}
643
644
static BOOL vgids_read_do_fkt(void* data, size_t index, va_list ap)
645
0
{
646
0
  BYTE* response = NULL;
647
0
  DWORD responseSize = 0;
648
0
  vgidsEF* file = (vgidsEF*)data;
649
0
  vgidsContext* context = va_arg(ap, vgidsContext*);
650
0
  UINT16 efID = (UINT16)va_arg(ap, unsigned);
651
0
  UINT16 doID = (UINT16)va_arg(ap, unsigned);
652
0
  WINPR_UNUSED(index);
653
654
0
  if (efID == 0x3FFF || efID == file->id)
655
0
  {
656
    /* If the DO was successfully read - abort file enum */
657
0
    if (vgids_ef_read_do(file, doID, &response, &responseSize))
658
0
    {
659
0
      context->responseData = Stream_New(response, (size_t)responseSize);
660
0
      return FALSE;
661
0
    }
662
0
  }
663
664
0
  return TRUE;
665
0
}
666
667
static void vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
668
0
{
669
0
  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 = NULL;
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 = NULL;
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 = NULL;
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, NULL, NULL, &p1, &p2, &lc, NULL))
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] = { 0 };
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 = NULL;
807
0
  wStream* response = NULL;
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(NULL, 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(NULL, 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_SetPosition(response, 0);
848
0
  context->responseData = response;
849
0
  response = NULL;
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 = NULL;
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, NULL, NULL, &p1, &p2, &lc, NULL))
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
      vgids_read_do(context, fileId, doId);
902
0
      break;
903
0
    }
904
0
    case 0xA:
905
0
    {
906
0
      UINT16 pubKeyDO = 0;
907
0
      BYTE tag = 0;
908
0
      BYTE length = 0;
909
0
      BYTE keyRef = 0;
910
911
      /* We want to retrieve the public key? */
912
0
      if (p1 != 0x3F && p2 != 0xFF)
913
0
      {
914
0
        status = ISO_STATUS_INVALIDP1P2;
915
0
        break;
916
0
      }
917
918
      /* read parent tag/length */
919
0
      Stream_Read_UINT8(s, tag);
920
0
      Stream_Read_UINT8(s, length);
921
0
      if (tag != 0x70 || length != 0x08)
922
0
      {
923
0
        status = ISO_STATUS_INVALIDCOMMANDDATA;
924
0
        break;
925
0
      }
926
927
      /* read key reference TLV */
928
0
      Stream_Read_UINT8(s, tag);
929
0
      Stream_Read_UINT8(s, length);
930
0
      Stream_Read_UINT8(s, keyRef);
931
0
      if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
932
0
      {
933
0
        status = ISO_STATUS_INVALIDCOMMANDDATA;
934
0
        break;
935
0
      }
936
937
      /* read key value template TLV */
938
0
      Stream_Read_UINT8(s, tag);
939
0
      Stream_Read_UINT8(s, length);
940
0
      if (tag != 0xA5 || length != 0x03)
941
0
      {
942
0
        status = ISO_STATUS_INVALIDCOMMANDDATA;
943
0
        break;
944
0
      }
945
946
0
      Stream_Read_UINT16_BE(s, pubKeyDO);
947
0
      Stream_Read_UINT8(s, length);
948
0
      if (pubKeyDO != 0x7F49 || length != 0x80)
949
0
      {
950
0
        status = ISO_STATUS_INVALIDCOMMANDDATA;
951
0
        break;
952
0
      }
953
954
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
955
0
      {
956
0
        status = ISO_STATUS_INVALIDLC;
957
0
        break;
958
0
      }
959
960
      /* Return public key value */
961
0
      vgids_get_public_key(context, pubKeyDO);
962
0
      break;
963
0
    }
964
0
    default:
965
0
      status = ISO_STATUS_INVALIDCOMMANDDATA;
966
0
      break;
967
0
  }
968
969
  /* If we have response data, make it ready for return */
970
0
  if (context->responseData)
971
0
    status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
972
0
  else if (status == ISO_STATUS_SUCCESS)
973
0
    status = ISO_STATUS_REFERENCEDATANOTFOUND;
974
975
0
  return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
976
0
}
977
978
static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s,
979
                                                  BYTE** response, DWORD* responseSize)
980
0
{
981
0
  BYTE tag = 0;
982
0
  BYTE length = 0;
983
0
  BYTE p1 = 0;
984
0
  BYTE p2 = 0;
985
0
  BYTE lc = 0;
986
0
  DWORD resultDataSize = 0;
987
0
  const BYTE* resultData = NULL;
988
0
  UINT16 status = ISO_STATUS_SUCCESS;
989
990
0
  vgids_reset_context_command_data(context);
991
0
  vgids_reset_context_response(context);
992
993
  /* Manage security environment prepares the card for performing crypto operations. */
994
0
  if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
995
0
    return FALSE;
996
997
  /* Check APDU params */
998
  /* P1: Set Computation, decipherment, Internal Auth */
999
  /* P2: Digital Signature (B6), Confidentiality (B8) */
1000
0
  if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
1001
0
  {
1002
0
    status = ISO_STATUS_INVALIDP1P2;
1003
0
    goto create_response;
1004
0
  }
1005
1006
0
  if (lc != 6)
1007
0
  {
1008
0
    status = ISO_STATUS_WRONGLC;
1009
0
    goto create_response;
1010
0
  }
1011
1012
0
  context->currentSE.crt = p2;
1013
1014
  /* parse command buffer */
1015
  /* Read algo ID */
1016
0
  Stream_Read_UINT8(s, tag);
1017
0
  Stream_Read_UINT8(s, length);
1018
0
  if (tag != 0x80 || length != 0x01)
1019
0
  {
1020
0
    status = ISO_STATUS_INVALIDCOMMANDDATA;
1021
0
    goto create_response;
1022
0
  }
1023
0
  Stream_Read_UINT8(s, context->currentSE.algoId);
1024
1025
  /* Read private key reference */
1026
0
  Stream_Read_UINT8(s, tag);
1027
0
  Stream_Read_UINT8(s, length);
1028
0
  if (tag != 0x84 || length != 0x01)
1029
0
  {
1030
0
    status = ISO_STATUS_INVALIDCOMMANDDATA;
1031
0
    goto create_response;
1032
0
  }
1033
0
  Stream_Read_UINT8(s, context->currentSE.keyRef);
1034
1035
0
create_response:
1036
  /* If an error occurred reset SE */
1037
0
  if (status != ISO_STATUS_SUCCESS)
1038
0
    memset(&context->currentSE, 0, sizeof(context->currentSE));
1039
0
  return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1040
0
}
1041
1042
static BOOL vgids_perform_digital_signature(vgidsContext* context)
1043
0
{
1044
0
  size_t sigSize = 0;
1045
0
  size_t msgSize = 0;
1046
0
  EVP_PKEY_CTX* ctx = NULL;
1047
0
  EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
1048
0
  const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
1049
0
    { g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() },
1050
0
    { g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() },
1051
0
    { g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() },
1052
0
    { g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() },
1053
0
    { g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() },
1054
0
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1055
0
    { g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
1056
0
    { g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
1057
0
#endif
1058
0
  };
1059
1060
0
  if (!pk)
1061
0
  {
1062
0
    WLog_ERR(TAG, "Failed to create PKEY");
1063
0
    return FALSE;
1064
0
  }
1065
1066
0
  vgids_reset_context_response(context);
1067
1068
  /* for each digest info */
1069
0
  Stream_SetPosition(context->commandData, 0);
1070
0
  for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
1071
0
  {
1072
    /* have we found our digest? */
1073
0
    const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
1074
0
    if (Stream_Length(context->commandData) >= digest->infoSize &&
1075
0
        memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
1076
0
    {
1077
      /* skip digest info and calculate message size */
1078
0
      Stream_Seek(context->commandData, digest->infoSize);
1079
0
      if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
1080
0
        goto sign_failed;
1081
0
      msgSize = Stream_GetRemainingLength(context->commandData);
1082
1083
      /* setup signing context */
1084
0
      ctx = EVP_PKEY_CTX_new(pk, NULL);
1085
0
      if (!ctx)
1086
0
      {
1087
0
        WLog_ERR(TAG, "Failed to create signing context");
1088
0
        goto sign_failed;
1089
0
      }
1090
1091
0
      if (EVP_PKEY_sign_init(ctx) <= 0)
1092
0
      {
1093
0
        WLog_ERR(TAG, "Failed to init signing context");
1094
0
        goto sign_failed;
1095
0
      }
1096
1097
      /* set padding and signature algo */
1098
0
      if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
1099
0
      {
1100
0
        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
1101
0
        {
1102
0
          WLog_ERR(TAG, "Failed to set padding mode");
1103
0
          goto sign_failed;
1104
0
        }
1105
0
      }
1106
1107
0
      if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
1108
0
      {
1109
0
        WLog_ERR(TAG, "Failed to set signing mode");
1110
0
        goto sign_failed;
1111
0
      }
1112
1113
      /* Determine buffer length */
1114
0
      if (EVP_PKEY_sign(ctx, NULL, &sigSize, Stream_Pointer(context->commandData), msgSize) <=
1115
0
          0)
1116
0
      {
1117
0
        WLog_ERR(TAG, "Failed to determine signature size");
1118
0
        goto sign_failed;
1119
0
      }
1120
1121
0
      context->responseData = Stream_New(NULL, sigSize);
1122
0
      if (!context->responseData)
1123
0
      {
1124
0
        WLog_ERR(TAG, "Failed to allocate signing buffer");
1125
0
        goto sign_failed;
1126
0
      }
1127
1128
      /* sign */
1129
0
      if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
1130
0
                        Stream_Pointer(context->commandData), msgSize) <= 0)
1131
0
      {
1132
0
        WLog_ERR(TAG, "Failed to create signature");
1133
0
        goto sign_failed;
1134
0
      }
1135
1136
0
      Stream_SetLength(context->responseData, sigSize);
1137
0
      EVP_PKEY_CTX_free(ctx);
1138
0
      break;
1139
0
    }
1140
0
  }
1141
1142
0
  EVP_PKEY_free(pk);
1143
0
  vgids_reset_context_command_data(context);
1144
0
  return TRUE;
1145
1146
0
sign_failed:
1147
0
  vgids_reset_context_command_data(context);
1148
0
  vgids_reset_context_response(context);
1149
0
  EVP_PKEY_CTX_free(ctx);
1150
0
  EVP_PKEY_free(pk);
1151
0
  return FALSE;
1152
0
}
1153
1154
static BOOL vgids_perform_decrypt(vgidsContext* context)
1155
0
{
1156
0
  EVP_PKEY_CTX* ctx = NULL;
1157
0
  BOOL rc = FALSE;
1158
0
  int res = 0;
1159
0
  int padding = RSA_NO_PADDING;
1160
1161
0
  vgids_reset_context_response(context);
1162
1163
  /* determine padding */
1164
0
  if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
1165
0
    padding = RSA_PKCS1_PADDING;
1166
0
  else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
1167
0
    padding = RSA_PKCS1_OAEP_PADDING;
1168
1169
  /* init response buffer */
1170
0
  EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
1171
0
  if (!pkey)
1172
0
    goto decrypt_failed;
1173
0
  ctx = EVP_PKEY_CTX_new(pkey, NULL);
1174
0
  if (!ctx)
1175
0
    goto decrypt_failed;
1176
0
  if (EVP_PKEY_decrypt_init(ctx) <= 0)
1177
0
    goto decrypt_failed;
1178
0
  if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
1179
0
    goto decrypt_failed;
1180
1181
  /* Determine buffer length */
1182
0
  const size_t inlen = Stream_Length(context->commandData);
1183
0
  size_t outlen = 0;
1184
0
  res = EVP_PKEY_decrypt(ctx, NULL, &outlen, Stream_Buffer(context->commandData), inlen);
1185
0
  if (res < 0)
1186
0
  {
1187
0
    WLog_ERR(TAG, "Failed to decrypt data");
1188
0
    goto decrypt_failed;
1189
0
  }
1190
1191
  /* Prepare output buffer */
1192
0
  context->responseData = Stream_New(NULL, outlen);
1193
0
  if (!context->responseData)
1194
0
  {
1195
0
    WLog_ERR(TAG, "Failed to create decryption buffer");
1196
0
    goto decrypt_failed;
1197
0
  }
1198
1199
  /* Decrypt */
1200
0
  res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1201
0
                         Stream_Buffer(context->commandData), inlen);
1202
1203
0
  if (res < 0)
1204
0
  {
1205
0
    WLog_ERR(TAG, "Failed to decrypt data");
1206
0
    goto decrypt_failed;
1207
0
  }
1208
1209
0
  Stream_SetLength(context->responseData, outlen);
1210
0
  rc = TRUE;
1211
1212
0
decrypt_failed:
1213
0
  EVP_PKEY_CTX_free(ctx);
1214
0
  EVP_PKEY_free(pkey);
1215
0
  vgids_reset_context_command_data(context);
1216
0
  if (!rc)
1217
0
    vgids_reset_context_response(context);
1218
0
  return rc;
1219
0
}
1220
1221
static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,
1222
                                                 DWORD* responseSize)
1223
0
{
1224
0
  BYTE cla = 0;
1225
0
  BYTE p1 = 0;
1226
0
  BYTE p2 = 0;
1227
0
  BYTE lc = 0;
1228
0
  DWORD resultDataSize = 0;
1229
0
  const BYTE* resultData = NULL;
1230
0
  UINT16 status = ISO_STATUS_SUCCESS;
1231
1232
  /* Perform security operation */
1233
0
  if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL))
1234
0
    return FALSE;
1235
1236
0
  if (lc == 0)
1237
0
  {
1238
0
    status = ISO_STATUS_WRONGLC;
1239
0
    goto create_response;
1240
0
  }
1241
1242
  /* Is our default key referenced? */
1243
0
  if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1244
0
  {
1245
0
    status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1246
0
    goto create_response;
1247
0
  }
1248
1249
  /* is the pin protecting the key verified? */
1250
0
  if (!context->pinVerified)
1251
0
  {
1252
0
    status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1253
0
    goto create_response;
1254
0
  }
1255
1256
  /* Append the data to the context command buffer (PSO might chain command data) */
1257
0
  if (!context->commandData)
1258
0
  {
1259
0
    context->commandData = Stream_New(NULL, lc);
1260
0
    if (!context->commandData)
1261
0
      return FALSE;
1262
0
  }
1263
0
  else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1264
0
    return FALSE;
1265
1266
0
  Stream_Write(context->commandData, Stream_Pointer(s), lc);
1267
0
  Stream_SealLength(context->commandData);
1268
1269
  /* Check if the correct operation is requested for our current SE */
1270
0
  switch (context->currentSE.crt)
1271
0
  {
1272
0
    case VGIDS_SE_CRT_SIGN:
1273
0
    {
1274
0
      if (p1 != 0x9E || p2 != 0x9A)
1275
0
      {
1276
0
        status = ISO_STATUS_INVALIDP1P2;
1277
0
        break;
1278
0
      }
1279
1280
      /* If chaining is over perform op */
1281
0
      if (!(cla & 0x10))
1282
0
        vgids_perform_digital_signature(context);
1283
0
      break;
1284
0
    }
1285
0
    case VGIDS_SE_CRT_CONF:
1286
0
    {
1287
0
      if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1288
0
      {
1289
0
        status = ISO_STATUS_INVALIDP1P2;
1290
0
        break;
1291
0
      }
1292
1293
      /* If chaining is over perform op */
1294
0
      if (!(cla & 0x10))
1295
0
        vgids_perform_decrypt(context);
1296
0
      break;
1297
0
    }
1298
0
    default:
1299
0
      status = ISO_STATUS_INVALIDP1P2;
1300
0
      break;
1301
0
  }
1302
1303
  /* Do chaining of response data if necessary */
1304
0
  if (status == ISO_STATUS_SUCCESS && context->responseData)
1305
0
    status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1306
1307
  /* Check APDU params */
1308
0
create_response:
1309
0
  return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1310
0
}
1311
1312
static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,
1313
                                  DWORD* responseSize)
1314
0
{
1315
0
  BYTE p1 = 0;
1316
0
  BYTE p2 = 0;
1317
0
  BYTE le = 0;
1318
0
  DWORD resultDataSize = 0;
1319
0
  const BYTE* resultData = NULL;
1320
0
  DWORD expectedLen = 0;
1321
0
  DWORD remainingSize = 0;
1322
0
  UINT16 status = ISO_STATUS_SUCCESS;
1323
1324
  /* Get response continues data transfer after a previous get data command */
1325
  /* Check if there is any data to transfer left */
1326
0
  if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1327
0
  {
1328
0
    status = ISO_STATUS_COMMANDNOTALLOWED;
1329
0
    goto create_response;
1330
0
  }
1331
1332
0
  if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le))
1333
0
    return FALSE;
1334
1335
  /* Check APDU params */
1336
0
  if (p1 != 00 || p2 != 0x00)
1337
0
  {
1338
0
    status = ISO_STATUS_INVALIDP1P2;
1339
0
    goto create_response;
1340
0
  }
1341
1342
  /* LE = 0 means 256 bytes expected */
1343
0
  expectedLen = le;
1344
0
  if (expectedLen == 0)
1345
0
    expectedLen = 256;
1346
1347
  /* prepare response size and update offset */
1348
0
  remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1349
0
  if (remainingSize < expectedLen)
1350
0
    expectedLen = remainingSize;
1351
1352
0
  resultData = Stream_Pointer(context->responseData);
1353
0
  resultDataSize = expectedLen;
1354
0
  Stream_Seek(context->responseData, expectedLen);
1355
1356
  /* If more data is left return 61XX - otherwise 9000 */
1357
0
  remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1358
0
  if (remainingSize > 0)
1359
0
  {
1360
0
    status = ISO_STATUS_MORE_DATA;
1361
0
    if (remainingSize < 256)
1362
0
      status |= (remainingSize & 0xFF);
1363
0
  }
1364
1365
0
create_response:
1366
0
  return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1367
0
}
1368
1369
static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,
1370
                             DWORD* responseSize)
1371
0
{
1372
0
  BYTE ins = 0;
1373
0
  BYTE p1 = 0;
1374
0
  BYTE p2 = 0;
1375
0
  BYTE lc = 0;
1376
0
  UINT16 status = ISO_STATUS_SUCCESS;
1377
0
  char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 };
1378
1379
  /* Verify is always called for the application password (PIN) P2=0x80 */
1380
0
  if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL))
1381
0
    return FALSE;
1382
1383
  /* Check APDU params */
1384
0
  if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1385
0
  {
1386
0
    status = ISO_STATUS_INVALIDP1P2;
1387
0
    goto create_response;
1388
0
  }
1389
1390
  /* shall we reset the security state? */
1391
0
  if (p2 == 0x82)
1392
0
  {
1393
0
    context->pinVerified = FALSE;
1394
0
    goto create_response;
1395
0
  }
1396
1397
  /* Check if pin is not already blocked */
1398
0
  if (context->curRetryCounter == 0)
1399
0
  {
1400
0
    status = ISO_STATUS_AUTHMETHODBLOCKED;
1401
0
    goto create_response;
1402
0
  }
1403
1404
  /* Read and verify LC */
1405
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1406
0
  {
1407
0
    status = ISO_STATUS_INVALIDLC;
1408
0
    goto create_response;
1409
0
  }
1410
1411
0
  Stream_Read_UINT8(s, lc);
1412
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1413
0
  {
1414
0
    status = ISO_STATUS_INVALIDLC;
1415
0
    goto create_response;
1416
0
  }
1417
1418
  /* read and verify pin */
1419
0
  Stream_Read(s, pin, lc);
1420
0
  if (strcmp(context->pin, pin) != 0)
1421
0
  {
1422
    /* retries are encoded in the lowest 4-bit of the status code */
1423
0
    --context->curRetryCounter;
1424
0
    context->pinVerified = FALSE;
1425
0
    status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
1426
0
  }
1427
0
  else
1428
0
  {
1429
    /* reset retry counter and mark pin as verified */
1430
0
    context->curRetryCounter = context->retryCounter;
1431
0
    context->pinVerified = TRUE;
1432
0
  }
1433
1434
0
create_response:
1435
0
  return vgids_create_response(status, NULL, 0, response, responseSize);
1436
0
}
1437
1438
vgidsContext* vgids_new(void)
1439
0
{
1440
0
  wObject* obj = NULL;
1441
0
  vgidsContext* ctx = calloc(1, sizeof(vgidsContext));
1442
1443
0
  ctx->files = ArrayList_New(FALSE);
1444
0
  if (!ctx->files)
1445
0
  {
1446
0
    WLog_ERR(TAG, "Failed to create files array list");
1447
0
    goto create_failed;
1448
0
  }
1449
1450
0
  obj = ArrayList_Object(ctx->files);
1451
0
  obj->fnObjectFree = vgids_ef_free;
1452
1453
0
  return ctx;
1454
1455
0
create_failed:
1456
0
  vgids_free(ctx);
1457
0
  return NULL;
1458
0
}
1459
1460
BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)
1461
0
{
1462
0
  DWORD kxcSize = 0;
1463
0
  DWORD keymapSize = 0;
1464
0
  DWORD fsTableSize = 0;
1465
0
  BOOL rc = FALSE;
1466
0
  BYTE* kxc = NULL;
1467
0
  BYTE* keymap = NULL;
1468
0
  BYTE* fsTable = NULL;
1469
0
  vgidsEF* masterEF = NULL;
1470
0
  vgidsEF* cardidEF = NULL;
1471
0
  vgidsEF* commonEF = NULL;
1472
0
  BYTE cardid[VGIDS_CARDID_SIZE] = { 0 };
1473
0
  vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ',
1474
0
                                   '0', '0' },
1475
0
                                 CONTAINER_MAP_VALID_CONTAINER |
1476
0
                                     CONTAINER_MAP_DEFAULT_CONTAINER,
1477
0
                                 0,
1478
0
                                 0,
1479
0
                                 0x00 /* key-size in bits - filled out later */ };
1480
0
  vgidsFilesysTableEntry filesys[] = {
1481
0
    { "mscp", "", 0, 0, 0, 0xA000, 0 },
1482
0
    { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 },
1483
0
    { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1484
0
    { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1485
0
    { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1486
0
    { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1487
0
  };
1488
1489
  /* Check params */
1490
0
  if (!cert || !privateKey || !pin)
1491
0
  {
1492
0
    WLog_DBG(TAG, "Passed invalid NULL argument: cert=%p, privateKey=%p, pin=%p", cert,
1493
0
             privateKey, pin);
1494
0
    goto init_failed;
1495
0
  }
1496
1497
  /* Convert PEM input to DER certificate/public key/private key */
1498
0
  ctx->certificate = freerdp_certificate_new_from_pem(cert);
1499
0
  if (!ctx->certificate)
1500
0
    goto init_failed;
1501
1502
0
  ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey, NULL);
1503
0
  if (!ctx->privateKey)
1504
0
    goto init_failed;
1505
1506
  /* create masterfile */
1507
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1508
0
  masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1509
0
  if (!masterEF)
1510
0
    goto init_failed;
1511
1512
  /* create cardid file with cardid DO */
1513
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1514
0
  cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1515
0
  if (!cardidEF)
1516
0
    goto init_failed;
1517
0
  winpr_RAND(cardid, sizeof(cardid));
1518
0
  if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))
1519
0
    goto init_failed;
1520
1521
  /* create user common file */
1522
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1523
0
  commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1524
0
  if (!commonEF)
1525
0
    goto init_failed;
1526
1527
  /* write card cache DO */
1528
0
  if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))
1529
0
    goto init_failed;
1530
1531
  /* write container map DO */
1532
0
  const size_t size = get_rsa_key_size(ctx->privateKey);
1533
0
  if ((size == 0) || (size > UINT16_MAX / 8))
1534
0
    goto init_failed;
1535
1536
0
  cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1537
0
  if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))
1538
0
    goto init_failed;
1539
1540
  /* write cardapps DO */
1541
0
  if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1542
0
                         sizeof(g_CardAppsContents)))
1543
0
    goto init_failed;
1544
1545
  /* convert and write certificate to key exchange container */
1546
0
  if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1547
0
    goto init_failed;
1548
0
  if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1549
0
    goto init_failed;
1550
1551
  /* prepare and write file system table */
1552
0
  if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1553
0
    goto init_failed;
1554
0
  if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1555
0
    goto init_failed;
1556
1557
  /* vgids_prepare_keymap and write to masterEF */
1558
0
  if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1559
0
    goto init_failed;
1560
0
  if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1561
0
    goto init_failed;
1562
1563
  /* store user pin */
1564
0
  ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
1565
0
  ctx->pin = _strdup(pin);
1566
0
  if (!ctx->pin)
1567
0
    goto init_failed;
1568
1569
0
  rc = TRUE;
1570
1571
0
init_failed:
1572
  // ArrayList_Append in vgids_ef_new takes ownership
1573
  // of cardidEF, commonEF, masterEF
1574
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
1575
0
  free(kxc);
1576
0
  free(keymap);
1577
0
  free(fsTable);
1578
0
  return rc;
1579
0
}
1580
1581
BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,
1582
                        DWORD* responseSize)
1583
0
{
1584
0
  wStream s;
1585
0
  static int x = 1;
1586
1587
  /* Check params */
1588
0
  if (!context || !data || !response || !responseSize)
1589
0
  {
1590
0
    WLog_ERR(TAG, "Invalid NULL pointer passed");
1591
0
    return FALSE;
1592
0
  }
1593
1594
0
  if (dataSize < 4)
1595
0
  {
1596
0
    WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1597
0
    return FALSE;
1598
0
  }
1599
1600
  /* Examine INS byte */
1601
0
  Stream_StaticConstInit(&s, data, dataSize);
1602
0
  if (x++ == 0xe)
1603
0
    x = 0xe + 1;
1604
0
  switch (data[1])
1605
0
  {
1606
0
    case ISO_INS_SELECT:
1607
0
      return vgids_ins_select(context, &s, response, responseSize);
1608
0
    case ISO_INS_GETDATA:
1609
0
      return vgids_ins_getdata(context, &s, response, responseSize);
1610
0
    case ISO_INS_GETRESPONSE:
1611
0
      return vgids_ins_getresponse(context, &s, response, responseSize);
1612
0
    case ISO_INS_MSE:
1613
0
      return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1614
0
    case ISO_INS_PSO:
1615
0
      return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1616
0
    case ISO_INS_VERIFY:
1617
0
      return vgids_ins_verify(context, &s, response, responseSize);
1618
0
    default:
1619
0
      break;
1620
0
  }
1621
1622
  /* return command not allowed */
1623
0
  return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize);
1624
0
}
1625
1626
void vgids_free(vgidsContext* context)
1627
0
{
1628
0
  if (context)
1629
0
  {
1630
0
    freerdp_key_free(context->privateKey);
1631
0
    freerdp_certificate_free(context->certificate);
1632
0
    Stream_Free(context->commandData, TRUE);
1633
0
    Stream_Free(context->responseData, TRUE);
1634
0
    free(context->pin);
1635
0
    ArrayList_Free(context->files);
1636
0
    free(context);
1637
0
  }
1638
0
}