2021-11-05 15:09:42 +03:00
|
|
|
|
/**
|
|
|
|
|
* WinPR: Windows Portable Runtime
|
|
|
|
|
* Virtual GIDS implementation
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2021 Martin Fleisz <martin.fleisz@thincast.com>
|
2023-02-09 10:48:11 +03:00
|
|
|
|
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
|
|
|
|
* Copyright 2021,2023 Thincast Technologies GmbH
|
2021-11-05 15:09:42 +03:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-02-16 13:20:38 +03:00
|
|
|
|
#include <freerdp/config.h>
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
#include <winpr/wlog.h>
|
|
|
|
|
#include <winpr/stream.h>
|
|
|
|
|
#include <winpr/collections.h>
|
|
|
|
|
|
2023-02-03 13:52:12 +03:00
|
|
|
|
#include <freerdp/crypto/crypto.h>
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
|
2023-02-03 13:52:12 +03:00
|
|
|
|
#include "../../crypto/certificate.h"
|
|
|
|
|
#include "../../crypto/privatekey.h"
|
2021-11-05 15:09:42 +03:00
|
|
|
|
#include "smartcard_virtual_gids.h"
|
|
|
|
|
|
|
|
|
|
#define TAG CHANNELS_TAG("smartcard.vgids")
|
|
|
|
|
|
|
|
|
|
#define VGIDS_EFID_MASTER 0xA000
|
|
|
|
|
#define VGIDS_EFID_COMMON 0xA010
|
2024-09-20 17:32:14 +03:00
|
|
|
|
// #define VGIDS_EFID_CARDCF VGIDS_EFID_COMMON
|
|
|
|
|
// #define VGIDS_EFID_CARDAPPS VGIDS_EFID_COMMON
|
|
|
|
|
// #define VGIDS_EFID_CMAPFILE VGIDS_EFID_COMMON
|
2021-11-05 15:09:42 +03:00
|
|
|
|
#define VGIDS_EFID_CARDID 0xA012
|
2024-09-20 17:32:14 +03:00
|
|
|
|
// #define VGIDS_EFID_KXC00 VGIDS_EFID_COMMON
|
2021-11-05 15:09:42 +03:00
|
|
|
|
#define VGIDS_EFID_CURRENTDF 0x3FFF
|
|
|
|
|
|
|
|
|
|
#define VGIDS_DO_FILESYSTEMTABLE 0xDF1F
|
|
|
|
|
#define VGIDS_DO_KEYMAP 0xDF20
|
|
|
|
|
#define VGIDS_DO_CARDID 0xDF20
|
|
|
|
|
#define VGIDS_DO_CARDAPPS 0xDF21
|
|
|
|
|
#define VGIDS_DO_CARDCF 0xDF22
|
|
|
|
|
#define VGIDS_DO_CMAPFILE 0xDF23
|
|
|
|
|
#define VGIDS_DO_KXC00 0xDF24
|
|
|
|
|
|
|
|
|
|
#define VGIDS_CARDID_SIZE 16
|
|
|
|
|
#define VGIDS_MAX_PIN_SIZE 127
|
|
|
|
|
|
|
|
|
|
#define VGIDS_DEFAULT_RETRY_COUNTER 3
|
|
|
|
|
|
|
|
|
|
#define VGIDS_KEY_TYPE_KEYEXCHANGE 0x9A
|
2024-09-20 17:32:14 +03:00
|
|
|
|
// #define VGIDS_KEY_TYPE_SIGNATURE 0x9C
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
#define VGIDS_ALGID_RSA_1024 0x06
|
|
|
|
|
#define VGIDS_ALGID_RSA_2048 0x07
|
|
|
|
|
#define VGIDS_ALGID_RSA_3072 0x08
|
|
|
|
|
#define VGIDS_ALGID_RSA_4096 0x09
|
|
|
|
|
|
2024-09-20 17:32:14 +03:00
|
|
|
|
// #define VGIDS_SE_CRT_AUTH 0xA4
|
2021-11-05 15:09:42 +03:00
|
|
|
|
#define VGIDS_SE_CRT_SIGN 0xB6
|
|
|
|
|
#define VGIDS_SE_CRT_CONF 0xB8
|
|
|
|
|
|
|
|
|
|
#define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40
|
|
|
|
|
#define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80
|
2024-09-20 17:32:14 +03:00
|
|
|
|
// #define VGIDS_SE_ALGOID_CT_RSA_1024 0x06
|
|
|
|
|
// #define VGIDS_SE_ALGOID_CT_RSA_2048 0x07
|
|
|
|
|
// #define VGIDS_SE_ALGOID_CT_RSA_3072 0x08
|
|
|
|
|
// #define VGIDS_SE_ALGOID_CT_RSA_4096 0x09
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_RSA_1024 0x06
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_RSA_2048 0x07
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_RSA_3072 0x08
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_RSA_4096 0x09
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_ECDSA_P192 0x0A
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_ECDSA_P224 0x0B
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_ECDSA_P256 0x0C
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_ECDSA_P384 0x0D
|
|
|
|
|
#define VGIDS_SE_ALGOID_DST_ECDSA_P512 0x0E
|
|
|
|
|
|
|
|
|
|
#define VGIDS_DEFAULT_KEY_REF 0x81
|
|
|
|
|
|
|
|
|
|
#define ISO_INS_SELECT 0xA4
|
|
|
|
|
#define ISO_INS_GETDATA 0xCB
|
|
|
|
|
#define ISO_INS_GETRESPONSE 0xC0
|
|
|
|
|
#define ISO_INS_MSE 0x22
|
|
|
|
|
#define ISO_INS_PSO 0x2A
|
|
|
|
|
#define ISO_INS_VERIFY 0x20
|
|
|
|
|
|
|
|
|
|
#define ISO_STATUS_MORE_DATA 0x6100
|
|
|
|
|
#define ISO_STATUS_VERIFYFAILED 0x6300
|
|
|
|
|
#define ISO_STATUS_WRONGLC 0x6700
|
|
|
|
|
#define ISO_STATUS_COMMANDNOTALLOWED 0x6900
|
|
|
|
|
#define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982
|
|
|
|
|
#define ISO_STATUS_AUTHMETHODBLOCKED 0x6983
|
|
|
|
|
#define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80
|
|
|
|
|
#define ISO_STATUS_FILENOTFOUND 0x6A82
|
|
|
|
|
#define ISO_STATUS_INVALIDP1P2 0x6A86
|
|
|
|
|
#define ISO_STATUS_INVALIDLC 0x6A87
|
|
|
|
|
#define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88
|
|
|
|
|
#define ISO_STATUS_SUCCESS 0x9000
|
|
|
|
|
|
|
|
|
|
#define ISO_AID_MAX_SIZE 16
|
|
|
|
|
|
|
|
|
|
#define ISO_FID_MF 0x3F00
|
|
|
|
|
|
|
|
|
|
struct vgids_ef
|
|
|
|
|
{
|
|
|
|
|
UINT16 id;
|
|
|
|
|
UINT16 dirID;
|
|
|
|
|
wStream* data;
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_ef vgidsEF;
|
|
|
|
|
|
|
|
|
|
struct vgids_se
|
|
|
|
|
{
|
|
|
|
|
BYTE crt; /* control reference template tag */
|
|
|
|
|
BYTE algoId; /* Algorithm ID */
|
|
|
|
|
BYTE keyRef; /* Key reference */
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_se vgidsSE;
|
|
|
|
|
|
|
|
|
|
struct vgids_context
|
|
|
|
|
{
|
|
|
|
|
UINT16 currentDF;
|
|
|
|
|
char* pin;
|
|
|
|
|
UINT16 curRetryCounter;
|
|
|
|
|
UINT16 retryCounter;
|
|
|
|
|
wStream* commandData;
|
|
|
|
|
wStream* responseData;
|
|
|
|
|
BOOL pinVerified;
|
|
|
|
|
vgidsSE currentSE;
|
|
|
|
|
|
2023-02-03 13:52:12 +03:00
|
|
|
|
rdpCertificate* certificate;
|
2023-02-03 17:15:18 +03:00
|
|
|
|
rdpPrivateKey* privateKey;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
wArrayList* files;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* PKCS 1.5 DER encoded digest information */
|
|
|
|
|
#define VGIDS_MAX_DIGEST_INFO 7
|
|
|
|
|
|
|
|
|
|
static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
|
|
|
|
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
|
|
|
|
|
static const BYTE g_PKCS1_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
|
|
|
|
0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
|
|
|
|
|
static const BYTE g_PKCS1_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
|
|
|
|
0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
|
|
|
|
|
static const BYTE g_PKCS1_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
|
|
|
|
0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
|
|
|
|
|
static const BYTE g_PKCS1_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
|
|
|
|
0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
|
|
|
|
|
static const BYTE g_PKCS1_SHA512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60,
|
|
|
|
|
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
|
|
|
|
|
0x05, 0x05, 0x00, 0x04, 0x1c };
|
|
|
|
|
static const BYTE g_PKCS1_SHA512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
|
|
|
|
|
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
|
|
|
|
|
0x06, 0x05, 0x00, 0x04, 0x20 };
|
|
|
|
|
|
|
|
|
|
/* Helper struct to map PKCS1.5 digest info to OpenSSL EVP_MD */
|
|
|
|
|
struct vgids_digest_info_map
|
|
|
|
|
{
|
|
|
|
|
const BYTE* info;
|
|
|
|
|
size_t infoSize;
|
|
|
|
|
const EVP_MD* digest;
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_digest_info_map vgidsDigestInfoMap;
|
|
|
|
|
|
|
|
|
|
/* MS GIDS AID */
|
|
|
|
|
/* xx: Used by the Windows smart card framework for the GIDS version number. This byte must be set
|
|
|
|
|
* to the GIDS specification revision number which is either 0x01 or 0x02.
|
|
|
|
|
* yy: Reserved for use by the card application (set to 01)
|
|
|
|
|
*/
|
|
|
|
|
static const BYTE g_MsGidsAID[] = {
|
|
|
|
|
0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* GIDS APP File Control Parameter:
|
|
|
|
|
FD-Byte (82): 38 (not shareable-DF)
|
|
|
|
|
Sec Attr (8C): 03 30 30 Create/Delete File(03) Ext/User-Auth (30)
|
|
|
|
|
*/
|
|
|
|
|
static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 };
|
|
|
|
|
/* GIDS APP File Control Information:
|
|
|
|
|
AppID (4F, Len 0B): A0 00 00 03 97 42 54 46 59 02 01
|
|
|
|
|
Discretionary DOs (73, Len 03): 40 01 C0
|
|
|
|
|
Supported Auth Protocols (40, Len 01): C0 Mutual/External-Auth
|
|
|
|
|
*/
|
|
|
|
|
static const BYTE g_GidsAppFCI[] = { 0x61, 0x12, 0x4F, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42,
|
|
|
|
|
0x54, 0x46, 0x59, 0x02, 0x01, 0x73, 0x03, 0x40, 0x01, 0xC0 };
|
|
|
|
|
|
|
|
|
|
/*
|
2022-02-14 16:59:22 +03:00
|
|
|
|
typedef struct
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
BYTE bVersion; // Cache version
|
|
|
|
|
BYTE bPinsFreshness; // Card PIN
|
|
|
|
|
WORD wContainersFreshness;
|
|
|
|
|
WORD wFilesFreshness;
|
|
|
|
|
} CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; */
|
|
|
|
|
static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 };
|
|
|
|
|
|
|
|
|
|
/* {mscp,0,0,0,0} */
|
|
|
|
|
static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
|
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
|
|
|
|
|
|
/* Type: CONTAINER_MAP_RECORD (taken from Windows Smart Card Minidriver Specification)
|
|
|
|
|
|
|
|
|
|
This structure describes the format of the Base CSP's
|
|
|
|
|
container map file, stored on the card. This is wellknown
|
|
|
|
|
logical file wszCONTAINER_MAP_FILE. The file consists of
|
|
|
|
|
zero or more of these records. */
|
|
|
|
|
#define MAX_CONTAINER_NAME_LEN 39
|
|
|
|
|
|
|
|
|
|
/* This flag is set in the CONTAINER_MAP_RECORD bFlags
|
|
|
|
|
member if the corresponding container is valid and currently
|
|
|
|
|
exists on the card. // If the container is deleted, its
|
|
|
|
|
bFlags field must be cleared. */
|
|
|
|
|
#define CONTAINER_MAP_VALID_CONTAINER 1
|
|
|
|
|
|
|
|
|
|
/* This flag is set in the CONTAINER_MAP_RECORD bFlags
|
|
|
|
|
member if the corresponding container is the default
|
|
|
|
|
container on the card. */
|
|
|
|
|
#define CONTAINER_MAP_DEFAULT_CONTAINER 2
|
|
|
|
|
|
|
|
|
|
struct vgids_container_map_entry
|
|
|
|
|
{
|
|
|
|
|
WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1];
|
|
|
|
|
BYTE bFlags;
|
|
|
|
|
BYTE bReserved;
|
|
|
|
|
WORD wSigKeySizeBits;
|
|
|
|
|
WORD wKeyExchangeKeySizeBits;
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_container_map_entry vgidsContainerMapEntry;
|
|
|
|
|
|
|
|
|
|
struct vgids_filesys_table_entry
|
|
|
|
|
{
|
|
|
|
|
char directory[9];
|
|
|
|
|
char filename[9];
|
|
|
|
|
UINT16 pad0;
|
|
|
|
|
UINT16 dataObjectIdentifier;
|
|
|
|
|
UINT16 pad1;
|
|
|
|
|
UINT16 fileIdentifier;
|
|
|
|
|
UINT16 unknown;
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry;
|
|
|
|
|
|
|
|
|
|
struct vgids_keymap_record
|
|
|
|
|
{
|
|
|
|
|
UINT32 state;
|
|
|
|
|
BYTE algid;
|
|
|
|
|
BYTE keytype;
|
|
|
|
|
UINT16 keyref;
|
|
|
|
|
UINT16 unknownWithFFFF;
|
|
|
|
|
UINT16 unknownWith0000;
|
|
|
|
|
};
|
|
|
|
|
typedef struct vgids_keymap_record vgidsKeymapRecord;
|
|
|
|
|
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
|
2024-01-24 10:21:47 +03:00
|
|
|
|
static void vgids_ef_free(void* ptr);
|
|
|
|
|
|
2021-11-05 15:09:42 +03:00
|
|
|
|
static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT id)
|
|
|
|
|
{
|
|
|
|
|
vgidsEF* ef = calloc(1, sizeof(vgidsEF));
|
|
|
|
|
|
|
|
|
|
ef->id = id;
|
|
|
|
|
ef->data = Stream_New(NULL, 1024);
|
|
|
|
|
if (!ef->data)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create file data stream");
|
|
|
|
|
goto create_failed;
|
|
|
|
|
}
|
|
|
|
|
Stream_SetLength(ef->data, 0);
|
|
|
|
|
|
|
|
|
|
if (!ArrayList_Append(ctx->files, ef))
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to add new ef to file list");
|
|
|
|
|
goto create_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ef;
|
|
|
|
|
|
|
|
|
|
create_failed:
|
2024-01-24 10:21:47 +03:00
|
|
|
|
vgids_ef_free(ef);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:00:11 +03:00
|
|
|
|
static BOOL vgids_write_tlv(wStream* s, UINT16 tag, const void* data, DWORD dataSize)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
/* A maximum of 5 additional bytes is needed */
|
|
|
|
|
if (!Stream_EnsureRemainingCapacity(s, dataSize + 5))
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to ensure capacity of DO stream");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* BER encoding: If the most-significant bit is set (0x80) the length is encoded in the
|
|
|
|
|
* remaining bits. So lengths < 128 bytes can be set directly, all others are encoded */
|
|
|
|
|
if (tag > 0xFF)
|
|
|
|
|
Stream_Write_UINT16_BE(s, tag);
|
|
|
|
|
else
|
|
|
|
|
Stream_Write_UINT8(s, (BYTE)tag);
|
|
|
|
|
if (dataSize < 128)
|
|
|
|
|
{
|
|
|
|
|
Stream_Write_UINT8(s, (BYTE)dataSize);
|
|
|
|
|
}
|
|
|
|
|
else if (dataSize < 256)
|
|
|
|
|
{
|
|
|
|
|
Stream_Write_UINT8(s, 0x81);
|
|
|
|
|
Stream_Write_UINT8(s, (BYTE)dataSize);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Stream_Write_UINT8(s, 0x82);
|
|
|
|
|
Stream_Write_UINT16_BE(s, (UINT16)dataSize);
|
|
|
|
|
}
|
|
|
|
|
Stream_Write(s, data, dataSize);
|
|
|
|
|
Stream_SealLength(s);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-16 09:00:11 +03:00
|
|
|
|
static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID, const void* data, DWORD dataSize)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
/* Write DO to end of file: 2-Byte ID, 1-Byte Len, Data */
|
|
|
|
|
return vgids_write_tlv(ef->data, doID, data, dataSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)
|
|
|
|
|
{
|
|
|
|
|
/* Read the given DO from the file: 2-Byte ID, 1-Byte Len, Data */
|
|
|
|
|
if (!Stream_SetPosition(ef->data, 0))
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to seek to front of file");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Look for the requested DO */
|
|
|
|
|
while (Stream_GetRemainingLength(ef->data) > 3)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE len = 0;
|
|
|
|
|
size_t curPos = 0;
|
|
|
|
|
UINT16 doSize = 0;
|
|
|
|
|
UINT16 nextDOID = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
curPos = Stream_GetPosition(ef->data);
|
|
|
|
|
Stream_Read_UINT16_BE(ef->data, nextDOID);
|
|
|
|
|
Stream_Read_UINT8(ef->data, len);
|
|
|
|
|
if ((len & 0x80))
|
|
|
|
|
{
|
|
|
|
|
BYTE lenSize = len & 0x7F;
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
switch (lenSize)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
Stream_Read_UINT8(ef->data, doSize);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
Stream_Read_UINT16_BE(ef->data, doSize);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
WLog_ERR(TAG, "Unexpected tag length %" PRIu8, lenSize);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
doSize = len;
|
|
|
|
|
|
2022-05-13 13:08:50 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (nextDOID == doID)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE* outData = NULL;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
/* Include Tag and length in result */
|
|
|
|
|
doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);
|
|
|
|
|
outData = malloc(doSize);
|
|
|
|
|
if (!outData)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate output buffer");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_SetPosition(ef->data, curPos);
|
|
|
|
|
Stream_Read(ef->data, outData, doSize);
|
|
|
|
|
*data = outData;
|
|
|
|
|
*dataSize = doSize;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Skip DO */
|
2024-09-14 21:55:48 +03:00
|
|
|
|
if (!Stream_SafeSeek(ef->data, doSize))
|
|
|
|
|
return FALSE;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-24 10:21:47 +03:00
|
|
|
|
void vgids_ef_free(void* ptr)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
vgidsEF* ef = ptr;
|
|
|
|
|
if (ef)
|
|
|
|
|
{
|
|
|
|
|
Stream_Free(ef->data, TRUE);
|
|
|
|
|
free(ef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_prepare_fstable(const vgidsFilesysTableEntry* fstable, DWORD numEntries,
|
|
|
|
|
BYTE** outData, DWORD* outDataSize)
|
|
|
|
|
{
|
|
|
|
|
/* Filesystem table:
|
|
|
|
|
BYTE unkonwn: 0x01
|
|
|
|
|
Array of vgidsFilesysTableEntry
|
|
|
|
|
*/
|
|
|
|
|
BYTE* data = malloc(sizeof(vgidsFilesysTableEntry) * numEntries + 1);
|
|
|
|
|
if (!data)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*data = 0x01;
|
2024-01-30 12:25:38 +03:00
|
|
|
|
for (UINT32 i = 0; i < numEntries; ++i)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
memcpy(data + 1 + (sizeof(vgidsFilesysTableEntry) * i), &fstable[i],
|
|
|
|
|
sizeof(vgidsFilesysTableEntry));
|
|
|
|
|
|
|
|
|
|
*outData = data;
|
|
|
|
|
*outDataSize = sizeof(vgidsFilesysTableEntry) * numEntries + 1;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-03 13:52:12 +03:00
|
|
|
|
static BOOL vgids_prepare_certificate(const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
/* Key exchange container:
|
|
|
|
|
UINT16 compression version: 0001
|
|
|
|
|
UINT16 source size
|
|
|
|
|
ZLIB compressed cert
|
|
|
|
|
*/
|
2024-01-23 18:49:54 +03:00
|
|
|
|
uLongf destSize = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
wStream* s = NULL;
|
|
|
|
|
BYTE* comprData = NULL;
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
|
|
|
|
WINPR_ASSERT(cert);
|
|
|
|
|
|
|
|
|
|
size_t certSize = 0;
|
|
|
|
|
BYTE* certData = freerdp_certificate_get_der(cert, &certSize);
|
|
|
|
|
if (!certData || (certSize == 0))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to get certificate size");
|
|
|
|
|
goto handle_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
comprData = malloc(certSize);
|
2023-02-03 13:52:12 +03:00
|
|
|
|
if (!comprData)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate certificate buffer");
|
|
|
|
|
goto handle_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* compress certificate data */
|
|
|
|
|
destSize = certSize;
|
|
|
|
|
if (compress(comprData, &destSize, certData, certSize) != Z_OK)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to compress certificate data");
|
|
|
|
|
goto handle_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write container data */
|
|
|
|
|
s = Stream_New(NULL, destSize + 4);
|
|
|
|
|
Stream_Write_UINT16(s, 0x0001);
|
|
|
|
|
Stream_Write_UINT16(s, (UINT16)certSize);
|
|
|
|
|
Stream_Write(s, comprData, destSize);
|
|
|
|
|
Stream_SealLength(s);
|
|
|
|
|
|
|
|
|
|
*kxc = Stream_Buffer(s);
|
|
|
|
|
*kxcSize = (DWORD)Stream_Length(s);
|
|
|
|
|
|
|
|
|
|
Stream_Free(s, FALSE);
|
|
|
|
|
free(certData);
|
|
|
|
|
free(comprData);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
handle_error:
|
|
|
|
|
Stream_Free(s, TRUE);
|
|
|
|
|
free(certData);
|
|
|
|
|
free(comprData);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 03:47:26 +03:00
|
|
|
|
static size_t get_rsa_key_size(const rdpPrivateKey* privateKey)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
2023-02-08 18:45:21 +03:00
|
|
|
|
WINPR_ASSERT(privateKey);
|
2023-02-08 16:32:24 +03:00
|
|
|
|
|
2023-04-26 12:09:11 +03:00
|
|
|
|
return freerdp_key_get_bits(privateKey) / 8;
|
2023-02-08 18:45:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BYTE vgids_get_algid(vgidsContext* p_Ctx)
|
|
|
|
|
{
|
|
|
|
|
WINPR_ASSERT(p_Ctx);
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
2023-02-08 18:45:21 +03:00
|
|
|
|
switch (get_rsa_key_size(p_Ctx->privateKey))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
case (1024 / 8):
|
|
|
|
|
return VGIDS_ALGID_RSA_1024;
|
|
|
|
|
case (2048 / 8):
|
|
|
|
|
return VGIDS_ALGID_RSA_2048;
|
|
|
|
|
case (3072 / 8):
|
|
|
|
|
return VGIDS_ALGID_RSA_3072;
|
|
|
|
|
case (4096 / 8):
|
|
|
|
|
return VGIDS_ALGID_RSA_4096;
|
|
|
|
|
default:
|
|
|
|
|
WLog_ERR(TAG, "Failed to determine algid for private key");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)
|
|
|
|
|
{
|
|
|
|
|
/* Key map record table:
|
|
|
|
|
BYTE unkonwn (count?): 0x01
|
|
|
|
|
Array of vgidsKeymapRecord
|
|
|
|
|
*/
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE* data = NULL;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
vgidsKeymapRecord record = {
|
|
|
|
|
1, /* state */
|
|
|
|
|
0, /* algo */
|
|
|
|
|
VGIDS_KEY_TYPE_KEYEXCHANGE, /* keytpe */
|
|
|
|
|
(0xB000 | VGIDS_DEFAULT_KEY_REF), /* keyref */
|
|
|
|
|
0xFFFF, /* unknown FFFF */
|
|
|
|
|
0x0000 /* unknown 0000 */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Determine algo */
|
|
|
|
|
BYTE algid = vgids_get_algid(context);
|
|
|
|
|
if (algid == 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
data = malloc(sizeof(record) + 1);
|
|
|
|
|
if (!data)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate filesystem table data blob");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*data = 0x01;
|
|
|
|
|
record.algid = algid;
|
|
|
|
|
memcpy(data + 1, &record, sizeof(record));
|
|
|
|
|
|
|
|
|
|
*outData = data;
|
|
|
|
|
*outDataSize = sizeof(record) + 1;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_parse_apdu_header(wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,
|
|
|
|
|
BYTE* le)
|
|
|
|
|
{
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Read and verify APDU data */
|
|
|
|
|
if (cla)
|
|
|
|
|
Stream_Read_UINT8(s, *cla);
|
|
|
|
|
else
|
|
|
|
|
Stream_Seek(s, 1);
|
|
|
|
|
if (ins)
|
|
|
|
|
Stream_Read_UINT8(s, *ins);
|
|
|
|
|
else
|
|
|
|
|
Stream_Seek(s, 1);
|
|
|
|
|
if (p1)
|
|
|
|
|
Stream_Read_UINT8(s, *p1);
|
|
|
|
|
else
|
|
|
|
|
Stream_Seek(s, 1);
|
|
|
|
|
if (p2)
|
|
|
|
|
Stream_Read_UINT8(s, *p2);
|
|
|
|
|
else
|
|
|
|
|
Stream_Seek(s, 1);
|
|
|
|
|
|
|
|
|
|
/* If LC is requested - check remaining length and read as well */
|
|
|
|
|
if (lc)
|
|
|
|
|
{
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
Stream_Read_UINT8(s, *lc);
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read LE */
|
|
|
|
|
if (le)
|
|
|
|
|
{
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
return FALSE;
|
|
|
|
|
Stream_Read_UINT8(s, *le);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_create_response(UINT16 status, const BYTE* answer, DWORD answerSize,
|
|
|
|
|
BYTE** outData, DWORD* outDataSize)
|
|
|
|
|
{
|
|
|
|
|
BYTE* out = malloc(answerSize + 2);
|
|
|
|
|
if (!out)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate memory for response data");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*outData = out;
|
|
|
|
|
if (answer)
|
|
|
|
|
{
|
|
|
|
|
memcpy(out, answer, answerSize);
|
|
|
|
|
out += answerSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out = (BYTE)((status >> 8) & 0xFF);
|
|
|
|
|
*(out + 1) = (BYTE)(status & 0xFF);
|
|
|
|
|
*outDataSize = answerSize + 2;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_read_do_fkt(void* data, size_t index, va_list ap)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE* response = NULL;
|
|
|
|
|
DWORD responseSize = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
vgidsEF* file = (vgidsEF*)data;
|
|
|
|
|
vgidsContext* context = va_arg(ap, vgidsContext*);
|
|
|
|
|
UINT16 efID = (UINT16)va_arg(ap, unsigned);
|
|
|
|
|
UINT16 doID = (UINT16)va_arg(ap, unsigned);
|
|
|
|
|
WINPR_UNUSED(index);
|
|
|
|
|
|
|
|
|
|
if (efID == 0x3FFF || efID == file->id)
|
|
|
|
|
{
|
|
|
|
|
/* If the DO was successfully read - abort file enum */
|
|
|
|
|
if (vgids_ef_read_do(file, doID, &response, &responseSize))
|
|
|
|
|
{
|
|
|
|
|
context->responseData = Stream_New(response, (size_t)responseSize);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
|
|
|
|
|
{
|
|
|
|
|
ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vgids_reset_context_response(vgidsContext* context)
|
|
|
|
|
{
|
|
|
|
|
Stream_Free(context->responseData, TRUE);
|
|
|
|
|
context->responseData = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vgids_reset_context_command_data(vgidsContext* context)
|
|
|
|
|
{
|
|
|
|
|
Stream_Free(context->commandData, TRUE);
|
|
|
|
|
context->commandData = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_select(vgidsContext* context, wStream* s, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE lc = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
DWORD resultDataSize = 0;
|
|
|
|
|
const BYTE* resultData = NULL;
|
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
/* The only select operations performed are either select by AID or select 3FFF (return
|
|
|
|
|
* information about the currently selected DF) */
|
|
|
|
|
if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Check P1 for selection mode */
|
|
|
|
|
switch (p1)
|
|
|
|
|
{
|
|
|
|
|
/* Select by AID */
|
|
|
|
|
case 0x04:
|
|
|
|
|
{
|
|
|
|
|
/* read AID from APDU */
|
|
|
|
|
BYTE aid[ISO_AID_MAX_SIZE] = { 0 };
|
|
|
|
|
if (lc > ISO_AID_MAX_SIZE)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "The LC byte is greater than the maximum AID length");
|
|
|
|
|
status = ISO_STATUS_INVALIDLC;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if we select MS GIDS App (only one we know) */
|
|
|
|
|
Stream_Read(s, aid, lc);
|
|
|
|
|
if (memcmp(aid, g_MsGidsAID, lc) != 0)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_FILENOTFOUND;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return FCI or FCP for MsGids App */
|
|
|
|
|
switch (p2)
|
|
|
|
|
{
|
|
|
|
|
/* Return FCI information */
|
|
|
|
|
case 0x00:
|
|
|
|
|
{
|
|
|
|
|
resultData = g_GidsAppFCI;
|
|
|
|
|
resultDataSize = sizeof(g_GidsAppFCI);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Return FCP information */
|
|
|
|
|
case 0x04:
|
|
|
|
|
{
|
|
|
|
|
resultData = g_GidsAppFCP;
|
|
|
|
|
resultDataSize = sizeof(g_GidsAppFCP);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (resultData)
|
|
|
|
|
context->currentDF = ISO_FID_MF;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Select by FID */
|
|
|
|
|
case 0x00:
|
|
|
|
|
{
|
|
|
|
|
/* read FID from APDU */
|
2024-01-23 18:49:54 +03:00
|
|
|
|
UINT16 fid = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (lc > 2)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "The LC byte for the file ID is greater than 2");
|
|
|
|
|
status = ISO_STATUS_INVALIDLC;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_Read_UINT16_BE(s, fid);
|
|
|
|
|
if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_FILENOTFOUND;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
/* P1 P2 combination not supported */
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static UINT16 vgids_handle_chained_response(vgidsContext* context, const BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
|
|
|
|
/* Cap to a maximum of 256 bytes and set status to more data */
|
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);
|
|
|
|
|
if (remainingBytes > 256)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_MORE_DATA;
|
|
|
|
|
remainingBytes = 256;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*response = Stream_Buffer(context->responseData);
|
|
|
|
|
*responseSize = remainingBytes;
|
|
|
|
|
Stream_Seek(context->responseData, remainingBytes);
|
|
|
|
|
|
|
|
|
|
/* Check if there are more than 256 bytes left or if we can already provide the remaining length
|
|
|
|
|
* in the status word */
|
|
|
|
|
remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);
|
|
|
|
|
if (remainingBytes < 256 && remainingBytes != 0)
|
|
|
|
|
status |= (remainingBytes & 0xFF);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)
|
|
|
|
|
{
|
2023-02-08 16:32:24 +03:00
|
|
|
|
BOOL rc = FALSE;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
wStream* pubKey = NULL;
|
|
|
|
|
wStream* response = NULL;
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
|
|
|
|
WINPR_ASSERT(context);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
/* Get key components */
|
2023-04-25 10:46:00 +03:00
|
|
|
|
size_t nSize = 0;
|
|
|
|
|
size_t eSize = 0;
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
2023-04-25 10:46:00 +03:00
|
|
|
|
char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);
|
|
|
|
|
char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
2023-04-25 10:46:00 +03:00
|
|
|
|
if (!n || !e)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto handle_error;
|
|
|
|
|
|
|
|
|
|
pubKey = Stream_New(NULL, nSize + eSize + 0x10);
|
|
|
|
|
if (!pubKey)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate public key stream");
|
|
|
|
|
goto handle_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response = Stream_New(NULL, Stream_Capacity(pubKey) + 0x10);
|
|
|
|
|
if (!response)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate response stream");
|
|
|
|
|
goto handle_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write modulus and exponent DOs */
|
2023-04-25 10:46:00 +03:00
|
|
|
|
if (!vgids_write_tlv(pubKey, 0x81, n, nSize))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto handle_error;
|
|
|
|
|
|
2023-04-25 10:46:00 +03:00
|
|
|
|
if (!vgids_write_tlv(pubKey, 0x82, e, eSize))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto handle_error;
|
|
|
|
|
|
|
|
|
|
/* write ISO public key template */
|
|
|
|
|
if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))
|
|
|
|
|
goto handle_error;
|
|
|
|
|
|
|
|
|
|
/* set response data */
|
|
|
|
|
Stream_SetPosition(response, 0);
|
|
|
|
|
context->responseData = response;
|
2024-04-11 13:56:33 +03:00
|
|
|
|
response = NULL;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
2023-02-08 16:32:24 +03:00
|
|
|
|
rc = TRUE;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
handle_error:
|
2023-04-25 10:46:00 +03:00
|
|
|
|
free(n);
|
|
|
|
|
free(e);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
Stream_Free(pubKey, TRUE);
|
2024-04-11 13:56:33 +03:00
|
|
|
|
Stream_Free(response, TRUE);
|
2023-02-08 16:32:24 +03:00
|
|
|
|
return rc;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_getdata(vgidsContext* context, wStream* s, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
UINT16 doId = 0;
|
|
|
|
|
UINT16 fileId = 0;
|
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE lc = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
DWORD resultDataSize = 0;
|
|
|
|
|
const BYTE* resultData = NULL;
|
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
/* GetData is called a lot!
|
|
|
|
|
- To retrieve DOs from files
|
|
|
|
|
- To retrieve public key information
|
|
|
|
|
*/
|
|
|
|
|
if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* free any previous queried data */
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
|
|
|
|
|
/* build up file identifier */
|
|
|
|
|
fileId = ((UINT16)p1 << 8) | p2;
|
|
|
|
|
|
|
|
|
|
/* Do we have a DO reference? */
|
|
|
|
|
switch (lc)
|
|
|
|
|
{
|
|
|
|
|
case 4:
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE tag = 0;
|
|
|
|
|
BYTE length = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (tag != 0x5C && length != 0x02)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_Read_UINT16_BE(s, doId);
|
|
|
|
|
vgids_read_do(context, fileId, doId);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0xA:
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
UINT16 pubKeyDO = 0;
|
|
|
|
|
BYTE tag = 0;
|
|
|
|
|
BYTE length = 0;
|
|
|
|
|
BYTE keyRef = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
/* We want to retrieve the public key? */
|
|
|
|
|
if (p1 != 0x3F && p2 != 0xFF)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read parent tag/length */
|
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (tag != 0x70 || length != 0x08)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read key reference TLV */
|
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
Stream_Read_UINT8(s, keyRef);
|
|
|
|
|
if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read key value template TLV */
|
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (tag != 0xA5 || length != 0x03)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_Read_UINT16_BE(s, pubKeyDO);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (pubKeyDO != 0x7F49 || length != 0x80)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDLC;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return public key value */
|
|
|
|
|
vgids_get_public_key(context, pubKeyDO);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we have response data, make it ready for return */
|
|
|
|
|
if (context->responseData)
|
|
|
|
|
status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
|
|
|
|
|
else if (status == ISO_STATUS_SUCCESS)
|
|
|
|
|
status = ISO_STATUS_REFERENCEDATANOTFOUND;
|
|
|
|
|
|
|
|
|
|
return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s,
|
|
|
|
|
BYTE** response, DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE tag = 0;
|
|
|
|
|
BYTE length = 0;
|
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE lc = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
DWORD resultDataSize = 0;
|
|
|
|
|
const BYTE* resultData = NULL;
|
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
vgids_reset_context_command_data(context);
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
|
|
|
|
|
/* Manage security environment prepares the card for performing crypto operations. */
|
|
|
|
|
if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Check APDU params */
|
|
|
|
|
/* P1: Set Computation, decipherment, Internal Auth */
|
|
|
|
|
/* P2: Digital Signature (B6), Confidentiality (B8) */
|
|
|
|
|
if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lc != 6)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_WRONGLC;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context->currentSE.crt = p2;
|
|
|
|
|
|
|
|
|
|
/* parse command buffer */
|
|
|
|
|
/* Read algo ID */
|
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (tag != 0x80 || length != 0x01)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
Stream_Read_UINT8(s, context->currentSE.algoId);
|
|
|
|
|
|
|
|
|
|
/* Read private key reference */
|
|
|
|
|
Stream_Read_UINT8(s, tag);
|
|
|
|
|
Stream_Read_UINT8(s, length);
|
|
|
|
|
if (tag != 0x84 || length != 0x01)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDCOMMANDDATA;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
Stream_Read_UINT8(s, context->currentSE.keyRef);
|
|
|
|
|
|
|
|
|
|
create_response:
|
|
|
|
|
/* If an error occured reset SE */
|
|
|
|
|
if (status != ISO_STATUS_SUCCESS)
|
|
|
|
|
memset(&context->currentSE, 0, sizeof(context->currentSE));
|
|
|
|
|
return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_perform_digital_signature(vgidsContext* context)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
size_t sigSize = 0;
|
|
|
|
|
size_t msgSize = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
EVP_PKEY_CTX* ctx = NULL;
|
2023-04-25 10:46:00 +03:00
|
|
|
|
EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
|
2023-02-03 13:52:12 +03:00
|
|
|
|
const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{ g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() },
|
|
|
|
|
{ g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() },
|
|
|
|
|
{ g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() },
|
|
|
|
|
{ g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() },
|
|
|
|
|
{ g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() },
|
2024-02-23 14:24:25 +03:00
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{ g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
|
|
|
|
|
{ g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
|
2024-02-23 14:24:25 +03:00
|
|
|
|
#endif
|
2021-11-05 15:09:42 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!pk)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create PKEY");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
|
|
|
|
|
/* for each digest info */
|
|
|
|
|
Stream_SetPosition(context->commandData, 0);
|
|
|
|
|
for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
|
|
|
|
|
{
|
|
|
|
|
/* have we found our digest? */
|
|
|
|
|
const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
|
|
|
|
|
if (Stream_Length(context->commandData) >= digest->infoSize &&
|
|
|
|
|
memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
|
|
|
|
|
{
|
|
|
|
|
/* skip digest info and calculate message size */
|
|
|
|
|
Stream_Seek(context->commandData, digest->infoSize);
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto sign_failed;
|
|
|
|
|
msgSize = Stream_GetRemainingLength(context->commandData);
|
|
|
|
|
|
|
|
|
|
/* setup signing context */
|
|
|
|
|
ctx = EVP_PKEY_CTX_new(pk, NULL);
|
|
|
|
|
if (!ctx)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create signing context");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (EVP_PKEY_sign_init(ctx) <= 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to init signing context");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set padding and signature algo */
|
|
|
|
|
if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
|
|
|
|
|
{
|
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to set padding mode");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to set signing mode");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine buffer length */
|
|
|
|
|
if (EVP_PKEY_sign(ctx, NULL, &sigSize, Stream_Pointer(context->commandData), msgSize) <=
|
|
|
|
|
0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to determine signature size");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context->responseData = Stream_New(NULL, sigSize);
|
|
|
|
|
if (!context->responseData)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to allocate signing buffer");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* sign */
|
|
|
|
|
if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
|
|
|
|
|
Stream_Pointer(context->commandData), msgSize) <= 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create signature");
|
|
|
|
|
goto sign_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_SetLength(context->responseData, sigSize);
|
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_free(pk);
|
|
|
|
|
vgids_reset_context_command_data(context);
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
sign_failed:
|
|
|
|
|
vgids_reset_context_command_data(context);
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
|
EVP_PKEY_free(pk);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_perform_decrypt(vgidsContext* context)
|
|
|
|
|
{
|
2023-04-26 12:09:11 +03:00
|
|
|
|
EVP_PKEY_CTX* ctx = NULL;
|
2023-02-08 16:32:24 +03:00
|
|
|
|
BOOL rc = FALSE;
|
2024-01-23 18:49:54 +03:00
|
|
|
|
int res = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
int padding = RSA_NO_PADDING;
|
|
|
|
|
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
|
|
|
|
|
/* determine padding */
|
|
|
|
|
if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
|
|
|
|
|
padding = RSA_PKCS1_PADDING;
|
|
|
|
|
else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
|
|
|
|
|
padding = RSA_PKCS1_OAEP_PADDING;
|
|
|
|
|
|
|
|
|
|
/* init response buffer */
|
2023-04-25 10:46:00 +03:00
|
|
|
|
EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
|
|
|
|
|
if (!pkey)
|
|
|
|
|
goto decrypt_failed;
|
2023-04-26 12:09:11 +03:00
|
|
|
|
ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
2023-04-25 10:46:00 +03:00
|
|
|
|
if (!ctx)
|
|
|
|
|
goto decrypt_failed;
|
|
|
|
|
if (EVP_PKEY_decrypt_init(ctx) <= 0)
|
|
|
|
|
goto decrypt_failed;
|
2023-04-26 12:09:11 +03:00
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
|
2023-02-08 16:32:24 +03:00
|
|
|
|
goto decrypt_failed;
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
2023-04-26 12:09:11 +03:00
|
|
|
|
/* Determine buffer length */
|
|
|
|
|
const size_t inlen = Stream_Length(context->commandData);
|
2023-04-27 17:30:04 +03:00
|
|
|
|
size_t outlen = 0;
|
|
|
|
|
res = EVP_PKEY_decrypt(ctx, NULL, &outlen, Stream_Buffer(context->commandData), inlen);
|
|
|
|
|
if (res < 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to decrypt data");
|
|
|
|
|
goto decrypt_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prepare output buffer */
|
2023-04-26 12:09:11 +03:00
|
|
|
|
context->responseData = Stream_New(NULL, outlen);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (!context->responseData)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create decryption buffer");
|
|
|
|
|
goto decrypt_failed;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 17:30:04 +03:00
|
|
|
|
/* Decrypt */
|
2023-04-25 10:46:00 +03:00
|
|
|
|
res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
|
|
|
|
|
Stream_Buffer(context->commandData), inlen);
|
2023-02-03 13:52:12 +03:00
|
|
|
|
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (res < 0)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to decrypt data");
|
|
|
|
|
goto decrypt_failed;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-25 10:46:00 +03:00
|
|
|
|
Stream_SetLength(context->responseData, outlen);
|
2023-02-08 16:32:24 +03:00
|
|
|
|
rc = TRUE;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
decrypt_failed:
|
2023-04-25 10:46:00 +03:00
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
|
EVP_PKEY_free(pkey);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
vgids_reset_context_command_data(context);
|
2023-02-08 16:32:24 +03:00
|
|
|
|
if (!rc)
|
|
|
|
|
vgids_reset_context_response(context);
|
|
|
|
|
return rc;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE cla = 0;
|
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE lc = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
DWORD resultDataSize = 0;
|
|
|
|
|
const BYTE* resultData = NULL;
|
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
/* Perform security operation */
|
|
|
|
|
if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (lc == 0)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_WRONGLC;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is our default key referenced? */
|
|
|
|
|
if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* is the pin protecting the key verified? */
|
|
|
|
|
if (!context->pinVerified)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the data to the context command buffer (PSO might chain command data) */
|
|
|
|
|
if (!context->commandData)
|
|
|
|
|
{
|
|
|
|
|
context->commandData = Stream_New(NULL, lc);
|
|
|
|
|
if (!context->commandData)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2024-09-16 06:16:18 +03:00
|
|
|
|
else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
|
|
|
|
|
return FALSE;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
|
|
|
|
|
Stream_Write(context->commandData, Stream_Pointer(s), lc);
|
|
|
|
|
Stream_SealLength(context->commandData);
|
|
|
|
|
|
|
|
|
|
/* Check if the correct operation is requested for our current SE */
|
|
|
|
|
switch (context->currentSE.crt)
|
|
|
|
|
{
|
|
|
|
|
case VGIDS_SE_CRT_SIGN:
|
|
|
|
|
{
|
|
|
|
|
if (p1 != 0x9E || p2 != 0x9A)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If chaining is over perform op */
|
|
|
|
|
if (!(cla & 0x10))
|
|
|
|
|
vgids_perform_digital_signature(context);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case VGIDS_SE_CRT_CONF:
|
|
|
|
|
{
|
|
|
|
|
if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If chaining is over perform op */
|
|
|
|
|
if (!(cla & 0x10))
|
|
|
|
|
vgids_perform_decrypt(context);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do chaining of response data if necessary */
|
|
|
|
|
if (status == ISO_STATUS_SUCCESS && context->responseData)
|
|
|
|
|
status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
|
|
|
|
|
|
|
|
|
|
/* Check APDU params */
|
|
|
|
|
create_response:
|
|
|
|
|
return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE le = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
DWORD resultDataSize = 0;
|
|
|
|
|
const BYTE* resultData = NULL;
|
2024-01-23 18:49:54 +03:00
|
|
|
|
DWORD expectedLen = 0;
|
|
|
|
|
DWORD remainingSize = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
/* Get response continues data transfer after a previous get data command */
|
|
|
|
|
/* Check if there is any data to transfer left */
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_COMMANDNOTALLOWED;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Check APDU params */
|
|
|
|
|
if (p1 != 00 || p2 != 0x00)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* LE = 0 means 256 bytes expected */
|
|
|
|
|
expectedLen = le;
|
|
|
|
|
if (expectedLen == 0)
|
|
|
|
|
expectedLen = 256;
|
|
|
|
|
|
|
|
|
|
/* prepare response size and update offset */
|
|
|
|
|
remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
|
|
|
|
|
if (remainingSize < expectedLen)
|
|
|
|
|
expectedLen = remainingSize;
|
|
|
|
|
|
|
|
|
|
resultData = Stream_Pointer(context->responseData);
|
|
|
|
|
resultDataSize = expectedLen;
|
|
|
|
|
Stream_Seek(context->responseData, expectedLen);
|
|
|
|
|
|
|
|
|
|
/* If more data is left return 61XX - otherwise 9000 */
|
|
|
|
|
remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
|
|
|
|
|
if (remainingSize > 0)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_MORE_DATA;
|
|
|
|
|
if (remainingSize < 256)
|
|
|
|
|
status |= (remainingSize & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create_response:
|
|
|
|
|
return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
BYTE ins = 0;
|
|
|
|
|
BYTE p1 = 0;
|
|
|
|
|
BYTE p2 = 0;
|
|
|
|
|
BYTE lc = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
UINT16 status = ISO_STATUS_SUCCESS;
|
|
|
|
|
char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 };
|
|
|
|
|
|
|
|
|
|
/* Verify is always called for the application password (PIN) P2=0x80 */
|
|
|
|
|
if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Check APDU params */
|
|
|
|
|
if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDP1P2;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* shall we reset the security state? */
|
|
|
|
|
if (p2 == 0x82)
|
|
|
|
|
{
|
|
|
|
|
context->pinVerified = FALSE;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if pin is not already blocked */
|
|
|
|
|
if (context->curRetryCounter == 0)
|
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_AUTHMETHODBLOCKED;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read and verify LC */
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDLC;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stream_Read_UINT8(s, lc);
|
2022-04-19 15:29:17 +03:00
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
|
|
|
|
status = ISO_STATUS_INVALIDLC;
|
|
|
|
|
goto create_response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* read and verify pin */
|
|
|
|
|
Stream_Read(s, pin, lc);
|
|
|
|
|
if (strcmp(context->pin, pin) != 0)
|
|
|
|
|
{
|
|
|
|
|
/* retries are encoded in the lowest 4-bit of the status code */
|
|
|
|
|
--context->curRetryCounter;
|
|
|
|
|
context->pinVerified = FALSE;
|
|
|
|
|
status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* reset retry counter and mark pin as verified */
|
|
|
|
|
context->curRetryCounter = context->retryCounter;
|
|
|
|
|
context->pinVerified = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create_response:
|
|
|
|
|
return vgids_create_response(status, NULL, 0, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 11:06:19 +03:00
|
|
|
|
vgidsContext* vgids_new(void)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
wObject* obj = NULL;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
vgidsContext* ctx = calloc(1, sizeof(vgidsContext));
|
|
|
|
|
|
|
|
|
|
ctx->files = ArrayList_New(FALSE);
|
|
|
|
|
if (!ctx->files)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Failed to create files array list");
|
|
|
|
|
goto create_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
obj = ArrayList_Object(ctx->files);
|
|
|
|
|
obj->fnObjectFree = vgids_ef_free;
|
|
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
|
|
|
|
|
|
create_failed:
|
|
|
|
|
vgids_free(ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)
|
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
|
DWORD kxcSize = 0;
|
|
|
|
|
DWORD keymapSize = 0;
|
|
|
|
|
DWORD fsTableSize = 0;
|
2021-11-05 15:09:42 +03:00
|
|
|
|
BOOL rc = FALSE;
|
|
|
|
|
BYTE* kxc = NULL;
|
|
|
|
|
BYTE* keymap = NULL;
|
|
|
|
|
BYTE* fsTable = NULL;
|
|
|
|
|
vgidsEF* masterEF = NULL;
|
|
|
|
|
vgidsEF* cardidEF = NULL;
|
|
|
|
|
vgidsEF* commonEF = NULL;
|
|
|
|
|
BYTE cardid[VGIDS_CARDID_SIZE] = { 0 };
|
|
|
|
|
vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ',
|
|
|
|
|
'0', '0' },
|
|
|
|
|
CONTAINER_MAP_VALID_CONTAINER |
|
|
|
|
|
CONTAINER_MAP_DEFAULT_CONTAINER,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0x00 /* key-size in bits - filled out later */ };
|
|
|
|
|
vgidsFilesysTableEntry filesys[] = {
|
|
|
|
|
{ "mscp", "", 0, 0, 0, 0xA000, 0 },
|
|
|
|
|
{ "", "cardid", 0, 0xDF20, 0, 0xA012, 0 },
|
|
|
|
|
{ "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 },
|
|
|
|
|
{ "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 },
|
|
|
|
|
{ "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
|
|
|
|
|
{ "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Check params */
|
|
|
|
|
if (!cert || !privateKey || !pin)
|
|
|
|
|
{
|
2022-10-10 12:17:57 +03:00
|
|
|
|
WLog_DBG(TAG, "Passed invalid NULL argument: cert=%p, privateKey=%p, pin=%p", cert,
|
|
|
|
|
privateKey, pin);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto init_failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert PEM input to DER certificate/public key/private key */
|
2023-02-03 13:52:12 +03:00
|
|
|
|
ctx->certificate = freerdp_certificate_new_from_pem(cert);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (!ctx->certificate)
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
2023-02-03 13:52:12 +03:00
|
|
|
|
ctx->privateKey = freerdp_key_new_from_pem(privateKey);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (!ctx->privateKey)
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* create masterfile */
|
|
|
|
|
masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
|
|
|
|
|
if (!masterEF)
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* create cardid file with cardid DO */
|
|
|
|
|
cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
|
|
|
|
|
if (!cardidEF)
|
|
|
|
|
goto init_failed;
|
2023-02-03 13:52:12 +03:00
|
|
|
|
winpr_RAND(cardid, sizeof(cardid));
|
2021-11-05 15:09:42 +03:00
|
|
|
|
if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* create user common file */
|
|
|
|
|
commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
|
|
|
|
|
if (!commonEF)
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* write card cache DO */
|
|
|
|
|
if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* write container map DO */
|
2024-09-25 03:47:26 +03:00
|
|
|
|
const size_t size = get_rsa_key_size(ctx->privateKey);
|
|
|
|
|
if ((size == 0) || (size > UINT16_MAX / 8))
|
2023-02-08 16:32:24 +03:00
|
|
|
|
goto init_failed;
|
|
|
|
|
|
2023-02-08 18:45:21 +03:00
|
|
|
|
cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
|
2023-05-16 09:00:11 +03:00
|
|
|
|
if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))
|
2021-11-05 15:09:42 +03:00
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* write cardapps DO */
|
|
|
|
|
if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
|
|
|
|
|
sizeof(g_CardAppsContents)))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* convert and write certificate to key exchange container */
|
|
|
|
|
if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* prepare and write file system table */
|
|
|
|
|
if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* vgids_prepare_keymap and write to masterEF */
|
|
|
|
|
if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
/* store user pin */
|
|
|
|
|
ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
|
|
|
|
|
ctx->pin = _strdup(pin);
|
|
|
|
|
if (!ctx->pin)
|
|
|
|
|
goto init_failed;
|
|
|
|
|
|
|
|
|
|
rc = TRUE;
|
|
|
|
|
|
|
|
|
|
init_failed:
|
2024-01-24 10:21:47 +03:00
|
|
|
|
// ArrayList_Append in vgids_ef_new takes ownership
|
|
|
|
|
// of cardidEF, commonEF, masterEF
|
|
|
|
|
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
|
2021-11-05 15:09:42 +03:00
|
|
|
|
free(kxc);
|
|
|
|
|
free(keymap);
|
|
|
|
|
free(fsTable);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,
|
|
|
|
|
DWORD* responseSize)
|
|
|
|
|
{
|
|
|
|
|
wStream s;
|
|
|
|
|
static int x = 1;
|
|
|
|
|
|
|
|
|
|
/* Check params */
|
|
|
|
|
if (!context || !data || !response || !responseSize)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "Invalid NULL pointer passed");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dataSize < 4)
|
|
|
|
|
{
|
|
|
|
|
WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Examine INS byte */
|
|
|
|
|
Stream_StaticConstInit(&s, data, dataSize);
|
|
|
|
|
if (x++ == 0xe)
|
|
|
|
|
x = 0xe + 1;
|
|
|
|
|
switch (data[1])
|
|
|
|
|
{
|
|
|
|
|
case ISO_INS_SELECT:
|
|
|
|
|
return vgids_ins_select(context, &s, response, responseSize);
|
|
|
|
|
case ISO_INS_GETDATA:
|
|
|
|
|
return vgids_ins_getdata(context, &s, response, responseSize);
|
|
|
|
|
case ISO_INS_GETRESPONSE:
|
|
|
|
|
return vgids_ins_getresponse(context, &s, response, responseSize);
|
|
|
|
|
case ISO_INS_MSE:
|
|
|
|
|
return vgids_ins_manage_security_environment(context, &s, response, responseSize);
|
|
|
|
|
case ISO_INS_PSO:
|
|
|
|
|
return vgids_ins_perform_security_operation(context, &s, response, responseSize);
|
|
|
|
|
case ISO_INS_VERIFY:
|
|
|
|
|
return vgids_ins_verify(context, &s, response, responseSize);
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* return command not allowed */
|
|
|
|
|
return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vgids_free(vgidsContext* context)
|
|
|
|
|
{
|
|
|
|
|
if (context)
|
|
|
|
|
{
|
2023-02-03 13:52:12 +03:00
|
|
|
|
freerdp_key_free(context->privateKey);
|
|
|
|
|
freerdp_certificate_free(context->certificate);
|
2021-11-05 15:09:42 +03:00
|
|
|
|
Stream_Free(context->commandData, TRUE);
|
|
|
|
|
Stream_Free(context->responseData, TRUE);
|
|
|
|
|
free(context->pin);
|
|
|
|
|
ArrayList_Free(context->files);
|
|
|
|
|
free(context);
|
|
|
|
|
}
|
|
|
|
|
}
|