Coverage Report

Created: 2024-05-20 06:11

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