22#include <freerdp/config.h>
24#include <winpr/wlog.h>
25#include <winpr/stream.h>
26#include <winpr/collections.h>
28#include <freerdp/crypto/crypto.h>
32#include "../../crypto/certificate.h"
33#include "../../crypto/privatekey.h"
34#include "smartcard_virtual_gids.h"
36#define TAG CHANNELS_TAG("smartcard.vgids")
38#define VGIDS_EFID_MASTER 0xA000
39#define VGIDS_EFID_COMMON 0xA010
43#define VGIDS_EFID_CARDID 0xA012
45#define VGIDS_EFID_CURRENTDF 0x3FFF
47#define VGIDS_DO_FILESYSTEMTABLE 0xDF1F
48#define VGIDS_DO_KEYMAP 0xDF20
49#define VGIDS_DO_CARDID 0xDF20
50#define VGIDS_DO_CARDAPPS 0xDF21
51#define VGIDS_DO_CARDCF 0xDF22
52#define VGIDS_DO_CMAPFILE 0xDF23
53#define VGIDS_DO_KXC00 0xDF24
55#define VGIDS_CARDID_SIZE 16
56#define VGIDS_MAX_PIN_SIZE 127
58#define VGIDS_DEFAULT_RETRY_COUNTER 3
60#define VGIDS_KEY_TYPE_KEYEXCHANGE 0x9A
63#define VGIDS_ALGID_RSA_1024 0x06
64#define VGIDS_ALGID_RSA_2048 0x07
65#define VGIDS_ALGID_RSA_3072 0x08
66#define VGIDS_ALGID_RSA_4096 0x09
69#define VGIDS_SE_CRT_SIGN 0xB6
70#define VGIDS_SE_CRT_CONF 0xB8
72#define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40
73#define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80
79#define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40
90#define VGIDS_DEFAULT_KEY_REF 0x81
92#define ISO_INS_SELECT 0xA4
93#define ISO_INS_GETDATA 0xCB
94#define ISO_INS_GETRESPONSE 0xC0
95#define ISO_INS_MSE 0x22
96#define ISO_INS_PSO 0x2A
97#define ISO_INS_VERIFY 0x20
99#define ISO_STATUS_MORE_DATA 0x6100
100#define ISO_STATUS_VERIFYFAILED 0x6300
101#define ISO_STATUS_WRONGLC 0x6700
102#define ISO_STATUS_COMMANDNOTALLOWED 0x6900
103#define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982
104#define ISO_STATUS_AUTHMETHODBLOCKED 0x6983
105#define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80
106#define ISO_STATUS_FILENOTFOUND 0x6A82
107#define ISO_STATUS_INVALIDP1P2 0x6A86
108#define ISO_STATUS_INVALIDLC 0x6A87
109#define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88
110#define ISO_STATUS_SUCCESS 0x9000
112#define ISO_AID_MAX_SIZE 16
114#define ISO_FID_MF 0x3F00
122typedef struct vgids_ef vgidsEF;
130typedef struct vgids_se vgidsSE;
136 UINT16 curRetryCounter;
143 rdpCertificate* certificate;
144 rdpPrivateKey* privateKey;
150#define VGIDS_MAX_DIGEST_INFO 7
152static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
153 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
154static 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 };
156static 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 };
158static 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 };
160static 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 };
162static 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 };
165static 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 };
170struct vgids_digest_info_map
174 const EVP_MD* digest;
176typedef struct vgids_digest_info_map vgidsDigestInfoMap;
183static const BYTE g_MsGidsAID[] = {
184 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01
191static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 };
197static 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 };
208static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 };
211static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 };
221#define MAX_CONTAINER_NAME_LEN 39
227#define CONTAINER_MAP_VALID_CONTAINER 1
232#define CONTAINER_MAP_DEFAULT_CONTAINER 2
234struct vgids_container_map_entry
236 WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1];
239 WORD wSigKeySizeBits;
240 WORD wKeyExchangeKeySizeBits;
242typedef struct vgids_container_map_entry vgidsContainerMapEntry;
244struct vgids_filesys_table_entry
249 UINT16 dataObjectIdentifier;
251 UINT16 fileIdentifier;
254typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry;
256struct vgids_keymap_record
262 UINT16 unknownWithFFFF;
263 UINT16 unknownWith0000;
265typedef struct vgids_keymap_record vgidsKeymapRecord;
269static void vgids_ef_free(
void* ptr);
271static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT
id)
273 vgidsEF* ef = calloc(1,
sizeof(vgidsEF));
276 ef->data = Stream_New(
nullptr, 1024);
279 WLog_ERR(TAG,
"Failed to create file data stream");
282 if (!Stream_SetLength(ef->data, 0))
285 if (!ArrayList_Append(ctx->files, ef))
287 WLog_ERR(TAG,
"Failed to add new ef to file list");
298static BOOL vgids_write_tlv(
wStream* s, UINT16 tag,
const void* data,
size_t dataSize)
300 WINPR_ASSERT(dataSize <= UINT16_MAX);
303 if (!Stream_EnsureRemainingCapacity(s, dataSize + 5))
305 WLog_ERR(TAG,
"Failed to ensure capacity of DO stream");
312 Stream_Write_UINT16_BE(s, tag);
314 Stream_Write_UINT8(s, (BYTE)tag);
317 Stream_Write_UINT8(s, (BYTE)dataSize);
319 else if (dataSize < 256)
321 Stream_Write_UINT8(s, 0x81);
322 Stream_Write_UINT8(s, (BYTE)dataSize);
326 Stream_Write_UINT8(s, 0x82);
327 Stream_Write_UINT16_BE(s, (UINT16)dataSize);
329 Stream_Write(s, data, dataSize);
330 Stream_SealLength(s);
334static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID,
const void* data, DWORD dataSize)
337 return vgids_write_tlv(ef->data, doID, data, dataSize);
340static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)
343 Stream_ResetPosition(ef->data);
346 while (Stream_GetRemainingLength(ef->data) > 3)
353 curPos = Stream_GetPosition(ef->data);
354 Stream_Read_UINT16_BE(ef->data, nextDOID);
355 Stream_Read_UINT8(ef->data, len);
358 BYTE lenSize = len & 0x7F;
359 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))
365 Stream_Read_UINT8(ef->data, doSize);
368 Stream_Read_UINT16_BE(ef->data, doSize);
371 WLog_ERR(TAG,
"Unexpected tag length %" PRIu8, lenSize);
378 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))
381 if (nextDOID == doID)
383 BYTE* outData =
nullptr;
386 doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);
387 if (!Stream_SetPosition(ef->data, curPos))
390 outData = malloc(doSize);
393 WLog_ERR(TAG,
"Failed to allocate output buffer");
397 Stream_Read(ef->data, outData, doSize);
405 if (!Stream_SafeSeek(ef->data, doSize))
413void vgids_ef_free(
void* ptr)
418 Stream_Free(ef->data, TRUE);
423static BOOL vgids_prepare_fstable(
const vgidsFilesysTableEntry* fstable, DWORD numEntries,
424 BYTE** outData, DWORD* outDataSize)
430 BYTE* data = malloc(
sizeof(vgidsFilesysTableEntry) * numEntries + 1);
433 WLog_ERR(TAG,
"Failed to allocate filesystem table data blob");
438 for (UINT32 i = 0; i < numEntries; ++i)
439 memcpy(data + 1 + (
sizeof(vgidsFilesysTableEntry) * i), &fstable[i],
440 sizeof(vgidsFilesysTableEntry));
443 *outDataSize =
sizeof(vgidsFilesysTableEntry) * numEntries + 1;
448static BOOL vgids_prepare_certificate(
const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)
457 BYTE* comprData =
nullptr;
462 BYTE* certData = freerdp_certificate_get_der(cert, &certSize);
463 if (!certData || (certSize == 0) || (certSize > UINT16_MAX))
465 WLog_ERR(TAG,
"Failed to get certificate size");
469 comprData = malloc(certSize);
472 WLog_ERR(TAG,
"Failed to allocate certificate buffer");
477 destSize = WINPR_ASSERTING_INT_CAST(uint16_t, certSize);
478 if (compress(comprData, &destSize, certData, WINPR_ASSERTING_INT_CAST(uint16_t, certSize)) !=
481 WLog_ERR(TAG,
"Failed to compress certificate data");
486 s = Stream_New(
nullptr, destSize + 4);
487 Stream_Write_UINT16(s, 0x0001);
488 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, certSize));
489 Stream_Write(s, comprData, destSize);
490 Stream_SealLength(s);
492 *kxc = Stream_Buffer(s);
493 *kxcSize = (DWORD)Stream_Length(s);
495 Stream_Free(s, FALSE);
501 Stream_Free(s, TRUE);
507static size_t get_rsa_key_size(
const rdpPrivateKey* privateKey)
509 WINPR_ASSERT(privateKey);
511 return freerdp_key_get_bits(privateKey) / 8;
514static BYTE vgids_get_algid(vgidsContext* p_Ctx)
518 switch (get_rsa_key_size(p_Ctx->privateKey))
521 return VGIDS_ALGID_RSA_1024;
523 return VGIDS_ALGID_RSA_2048;
525 return VGIDS_ALGID_RSA_3072;
527 return VGIDS_ALGID_RSA_4096;
529 WLog_ERR(TAG,
"Failed to determine algid for private key");
536static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)
542 BYTE* data =
nullptr;
543 vgidsKeymapRecord record = {
546 VGIDS_KEY_TYPE_KEYEXCHANGE,
547 (0xB000 | VGIDS_DEFAULT_KEY_REF),
553 BYTE algid = vgids_get_algid(context);
557 data = malloc(
sizeof(record) + 1);
560 WLog_ERR(TAG,
"Failed to allocate filesystem table data blob");
565 record.algid = algid;
566 memcpy(data + 1, &record,
sizeof(record));
569 *outDataSize =
sizeof(record) + 1;
574static BOOL vgids_parse_apdu_header(
wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,
577 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
582 Stream_Read_UINT8(s, *cla);
586 Stream_Read_UINT8(s, *ins);
590 Stream_Read_UINT8(s, *p1);
594 Stream_Read_UINT8(s, *p2);
601 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
604 Stream_Read_UINT8(s, *lc);
605 if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))
612 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
614 Stream_Read_UINT8(s, *le);
620static BOOL vgids_create_response(UINT16 status,
const BYTE* answer, DWORD answerSize,
621 BYTE** outData, DWORD* outDataSize)
623 BYTE* out = malloc(answerSize + 2);
626 WLog_ERR(TAG,
"Failed to allocate memory for response data");
633 memcpy(out, answer, answerSize);
637 *out = (BYTE)((status >> 8) & 0xFF);
638 *(out + 1) = (BYTE)(status & 0xFF);
639 *outDataSize = answerSize + 2;
643static BOOL vgids_read_do_fkt(
void* data,
size_t index, va_list ap)
645 BYTE* response =
nullptr;
646 DWORD responseSize = 0;
647 vgidsEF* file = (vgidsEF*)data;
648 vgidsContext* context = va_arg(ap, vgidsContext*);
649 UINT16 efID = (UINT16)va_arg(ap,
unsigned);
650 UINT16 doID = (UINT16)va_arg(ap,
unsigned);
653 if (efID == 0x3FFF || efID == file->id)
656 if (vgids_ef_read_do(file, doID, &response, &responseSize))
658 context->responseData = Stream_New(response, (
size_t)responseSize);
659 if (!context->responseData)
668static BOOL vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
670 return ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);
673static void vgids_reset_context_response(vgidsContext* context)
675 Stream_Free(context->responseData, TRUE);
676 context->responseData =
nullptr;
679static void vgids_reset_context_command_data(vgidsContext* context)
681 Stream_Free(context->commandData, TRUE);
682 context->commandData =
nullptr;
685static BOOL vgids_ins_select(vgidsContext* context,
wStream* s, BYTE** response,
691 DWORD resultDataSize = 0;
692 const BYTE* resultData =
nullptr;
693 UINT16 status = ISO_STATUS_SUCCESS;
697 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
707 BYTE aid[ISO_AID_MAX_SIZE] = WINPR_C_ARRAY_INIT;
708 if (lc > ISO_AID_MAX_SIZE)
710 WLog_ERR(TAG,
"The LC byte is greater than the maximum AID length");
711 status = ISO_STATUS_INVALIDLC;
716 Stream_Read(s, aid, lc);
717 if (memcmp(aid, g_MsGidsAID, lc) != 0)
719 status = ISO_STATUS_FILENOTFOUND;
729 resultData = g_GidsAppFCI;
730 resultDataSize =
sizeof(g_GidsAppFCI);
736 resultData = g_GidsAppFCP;
737 resultDataSize =
sizeof(g_GidsAppFCP);
741 status = ISO_STATUS_INVALIDP1P2;
746 context->currentDF = ISO_FID_MF;
756 WLog_ERR(TAG,
"The LC byte for the file ID is greater than 2");
757 status = ISO_STATUS_INVALIDLC;
761 Stream_Read_UINT16_BE(s, fid);
762 if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)
764 status = ISO_STATUS_FILENOTFOUND;
772 status = ISO_STATUS_INVALIDP1P2;
777 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
780static UINT16 vgids_handle_chained_response(vgidsContext* context,
const BYTE** response,
784 UINT16 status = ISO_STATUS_SUCCESS;
785 DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);
786 if (remainingBytes > 256)
788 status = ISO_STATUS_MORE_DATA;
789 remainingBytes = 256;
792 *response = Stream_Buffer(context->responseData);
793 *responseSize = remainingBytes;
794 Stream_Seek(context->responseData, remainingBytes);
798 remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);
799 if (remainingBytes < 256 && remainingBytes != 0)
800 status |= (remainingBytes & 0xFF);
804static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)
810 WINPR_ASSERT(context);
816 char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);
817 char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);
822 pubKey = Stream_New(
nullptr, nSize + eSize + 0x10);
825 WLog_ERR(TAG,
"Failed to allocate public key stream");
829 response = Stream_New(
nullptr, Stream_Capacity(pubKey) + 0x10);
832 WLog_ERR(TAG,
"Failed to allocate response stream");
837 if (!vgids_write_tlv(pubKey, 0x81, n, nSize))
840 if (!vgids_write_tlv(pubKey, 0x82, e, eSize))
844 if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))
848 Stream_ResetPosition(response);
849 context->responseData = response;
856 Stream_Free(pubKey, TRUE);
857 Stream_Free(response, TRUE);
861static BOOL vgids_ins_getdata(vgidsContext* context,
wStream* s, BYTE** response,
869 DWORD resultDataSize = 0;
870 const BYTE* resultData =
nullptr;
871 UINT16 status = ISO_STATUS_SUCCESS;
877 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
881 vgids_reset_context_response(context);
884 fileId = (UINT16)(((UINT16)p1 << 8) | p2);
893 Stream_Read_UINT8(s, tag);
894 Stream_Read_UINT8(s, length);
895 if (tag != 0x5C && length != 0x02)
897 status = ISO_STATUS_INVALIDCOMMANDDATA;
901 Stream_Read_UINT16_BE(s, doId);
905 (void)vgids_read_do(context, fileId, doId);
916 if (p1 != 0x3F && p2 != 0xFF)
918 status = ISO_STATUS_INVALIDP1P2;
923 Stream_Read_UINT8(s, tag);
924 Stream_Read_UINT8(s, length);
925 if (tag != 0x70 || length != 0x08)
927 status = ISO_STATUS_INVALIDCOMMANDDATA;
932 Stream_Read_UINT8(s, tag);
933 Stream_Read_UINT8(s, length);
934 Stream_Read_UINT8(s, keyRef);
935 if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
937 status = ISO_STATUS_INVALIDCOMMANDDATA;
942 Stream_Read_UINT8(s, tag);
943 Stream_Read_UINT8(s, length);
944 if (tag != 0xA5 || length != 0x03)
946 status = ISO_STATUS_INVALIDCOMMANDDATA;
950 Stream_Read_UINT16_BE(s, pubKeyDO);
951 Stream_Read_UINT8(s, length);
952 if (pubKeyDO != 0x7F49 || length != 0x80)
954 status = ISO_STATUS_INVALIDCOMMANDDATA;
958 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
960 status = ISO_STATUS_INVALIDLC;
965 vgids_get_public_key(context, pubKeyDO);
969 status = ISO_STATUS_INVALIDCOMMANDDATA;
974 if (context->responseData)
975 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
976 else if (status == ISO_STATUS_SUCCESS)
977 status = ISO_STATUS_REFERENCEDATANOTFOUND;
979 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
982static BOOL vgids_ins_manage_security_environment(vgidsContext* context,
wStream* s,
983 BYTE** response, DWORD* responseSize)
990 DWORD resultDataSize = 0;
991 const BYTE* resultData =
nullptr;
992 UINT16 status = ISO_STATUS_SUCCESS;
994 vgids_reset_context_command_data(context);
995 vgids_reset_context_response(context);
998 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
1004 if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
1006 status = ISO_STATUS_INVALIDP1P2;
1007 goto create_response;
1012 status = ISO_STATUS_WRONGLC;
1013 goto create_response;
1016 context->currentSE.crt = p2;
1020 Stream_Read_UINT8(s, tag);
1021 Stream_Read_UINT8(s, length);
1022 if (tag != 0x80 || length != 0x01)
1024 status = ISO_STATUS_INVALIDCOMMANDDATA;
1025 goto create_response;
1027 Stream_Read_UINT8(s, context->currentSE.algoId);
1030 Stream_Read_UINT8(s, tag);
1031 Stream_Read_UINT8(s, length);
1032 if (tag != 0x84 || length != 0x01)
1034 status = ISO_STATUS_INVALIDCOMMANDDATA;
1035 goto create_response;
1037 Stream_Read_UINT8(s, context->currentSE.keyRef);
1041 if (status != ISO_STATUS_SUCCESS)
1042 memset(&context->currentSE, 0,
sizeof(context->currentSE));
1043 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1046static BOOL vgids_perform_digital_signature(vgidsContext* context)
1050 EVP_PKEY_CTX* ctx =
nullptr;
1051 EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
1052 const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
1053 { g_PKCS1_SHA1,
sizeof(g_PKCS1_SHA1), EVP_sha1() },
1054 { g_PKCS1_SHA224,
sizeof(g_PKCS1_SHA224), EVP_sha224() },
1055 { g_PKCS1_SHA256,
sizeof(g_PKCS1_SHA256), EVP_sha256() },
1056 { g_PKCS1_SHA384,
sizeof(g_PKCS1_SHA384), EVP_sha384() },
1057 { g_PKCS1_SHA512,
sizeof(g_PKCS1_SHA512), EVP_sha512() },
1058#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1059 { g_PKCS1_SHA512_224,
sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
1060 { g_PKCS1_SHA512_256,
sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
1066 WLog_ERR(TAG,
"Failed to create PKEY");
1070 vgids_reset_context_response(context);
1073 Stream_ResetPosition(context->commandData);
1074 for (
int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
1077 const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
1078 if (Stream_Length(context->commandData) >= digest->infoSize &&
1079 memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
1082 Stream_Seek(context->commandData, digest->infoSize);
1083 if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
1085 msgSize = Stream_GetRemainingLength(context->commandData);
1088 ctx = EVP_PKEY_CTX_new(pk,
nullptr);
1091 WLog_ERR(TAG,
"Failed to create signing context");
1095 if (EVP_PKEY_sign_init(ctx) <= 0)
1097 WLog_ERR(TAG,
"Failed to init signing context");
1102 if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
1104 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
1106 WLog_ERR(TAG,
"Failed to set padding mode");
1111 if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
1113 WLog_ERR(TAG,
"Failed to set signing mode");
1118 if (EVP_PKEY_sign(ctx,
nullptr, &sigSize, Stream_Pointer(context->commandData),
1121 WLog_ERR(TAG,
"Failed to determine signature size");
1125 context->responseData = Stream_New(
nullptr, sigSize);
1126 if (!context->responseData)
1128 WLog_ERR(TAG,
"Failed to allocate signing buffer");
1133 if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
1134 Stream_Pointer(context->commandData), msgSize) <= 0)
1136 WLog_ERR(TAG,
"Failed to create signature");
1140 if (!Stream_SetLength(context->responseData, sigSize))
1143 EVP_PKEY_CTX_free(ctx);
1149 vgids_reset_context_command_data(context);
1153 vgids_reset_context_command_data(context);
1154 vgids_reset_context_response(context);
1155 EVP_PKEY_CTX_free(ctx);
1160static BOOL vgids_perform_decrypt(vgidsContext* context)
1162 EVP_PKEY_CTX* ctx =
nullptr;
1165 int padding = RSA_NO_PADDING;
1167 vgids_reset_context_response(context);
1170 if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
1171 padding = RSA_PKCS1_PADDING;
1172 else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
1173 padding = RSA_PKCS1_OAEP_PADDING;
1176 EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
1178 goto decrypt_failed;
1179 ctx = EVP_PKEY_CTX_new(pkey,
nullptr);
1181 goto decrypt_failed;
1182 if (EVP_PKEY_decrypt_init(ctx) <= 0)
1183 goto decrypt_failed;
1184 if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
1185 goto decrypt_failed;
1189 const size_t inlen = Stream_Length(context->commandData);
1191 res = EVP_PKEY_decrypt(ctx,
nullptr, &outlen, Stream_Buffer(context->commandData), inlen);
1194 WLog_ERR(TAG,
"Failed to decrypt data");
1195 goto decrypt_failed;
1199 context->responseData = Stream_New(
nullptr, outlen);
1201 if (!context->responseData)
1203 WLog_ERR(TAG,
"Failed to create decryption buffer");
1204 goto decrypt_failed;
1208 res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1209 Stream_Buffer(context->commandData), inlen);
1213 WLog_ERR(TAG,
"Failed to decrypt data");
1214 goto decrypt_failed;
1217 rc = Stream_SetLength(context->responseData, outlen);
1221 EVP_PKEY_CTX_free(ctx);
1222 EVP_PKEY_free(pkey);
1223 vgids_reset_context_command_data(context);
1225 vgids_reset_context_response(context);
1229static BOOL vgids_ins_perform_security_operation(vgidsContext* context,
wStream* s, BYTE** response,
1230 DWORD* responseSize)
1236 DWORD resultDataSize = 0;
1237 const BYTE* resultData =
nullptr;
1238 UINT16 status = ISO_STATUS_SUCCESS;
1241 if (!vgids_parse_apdu_header(s, &cla,
nullptr, &p1, &p2, &lc,
nullptr))
1246 status = ISO_STATUS_WRONGLC;
1247 goto create_response;
1251 if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1253 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1254 goto create_response;
1258 if (!context->pinVerified)
1260 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1261 goto create_response;
1265 if (!context->commandData)
1267 context->commandData = Stream_New(
nullptr, lc);
1268 if (!context->commandData)
1271 else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1274 Stream_Write(context->commandData, Stream_Pointer(s), lc);
1275 Stream_SealLength(context->commandData);
1278 switch (context->currentSE.crt)
1280 case VGIDS_SE_CRT_SIGN:
1282 if (p1 != 0x9E || p2 != 0x9A)
1284 status = ISO_STATUS_INVALIDP1P2;
1290 vgids_perform_digital_signature(context);
1293 case VGIDS_SE_CRT_CONF:
1295 if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1297 status = ISO_STATUS_INVALIDP1P2;
1303 vgids_perform_decrypt(context);
1307 status = ISO_STATUS_INVALIDP1P2;
1312 if (status == ISO_STATUS_SUCCESS && context->responseData)
1313 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1317 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1320static BOOL vgids_ins_getresponse(vgidsContext* context,
wStream* s, BYTE** response,
1321 DWORD* responseSize)
1326 DWORD resultDataSize = 0;
1327 const BYTE* resultData =
nullptr;
1328 DWORD expectedLen = 0;
1329 DWORD remainingSize = 0;
1330 UINT16 status = ISO_STATUS_SUCCESS;
1334 if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1336 status = ISO_STATUS_COMMANDNOTALLOWED;
1337 goto create_response;
1340 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2,
nullptr, &le))
1344 if (p1 != 00 || p2 != 0x00)
1346 status = ISO_STATUS_INVALIDP1P2;
1347 goto create_response;
1352 if (expectedLen == 0)
1356 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1357 if (remainingSize < expectedLen)
1358 expectedLen = remainingSize;
1360 resultData = Stream_Pointer(context->responseData);
1361 resultDataSize = expectedLen;
1362 Stream_Seek(context->responseData, expectedLen);
1365 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1366 if (remainingSize > 0)
1368 status = ISO_STATUS_MORE_DATA;
1369 if (remainingSize < 256)
1370 status |= (remainingSize & 0xFF);
1374 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1377static BOOL vgids_ins_verify(vgidsContext* context,
wStream* s, BYTE** response,
1378 DWORD* responseSize)
1384 UINT16 status = ISO_STATUS_SUCCESS;
1385 char pin[VGIDS_MAX_PIN_SIZE + 1] = WINPR_C_ARRAY_INIT;
1388 if (!vgids_parse_apdu_header(s,
nullptr, &ins, &p1, &p2,
nullptr,
nullptr))
1392 if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1394 status = ISO_STATUS_INVALIDP1P2;
1395 goto create_response;
1401 context->pinVerified = FALSE;
1402 goto create_response;
1406 if (context->curRetryCounter == 0)
1408 status = ISO_STATUS_AUTHMETHODBLOCKED;
1409 goto create_response;
1413 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1415 status = ISO_STATUS_INVALIDLC;
1416 goto create_response;
1419 Stream_Read_UINT8(s, lc);
1420 if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1422 status = ISO_STATUS_INVALIDLC;
1423 goto create_response;
1427 Stream_Read(s, pin, lc);
1428 if (strcmp(context->pin, pin) != 0)
1431 --context->curRetryCounter;
1432 context->pinVerified = FALSE;
1433 status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));
1438 context->curRetryCounter = context->retryCounter;
1439 context->pinVerified = TRUE;
1443 return vgids_create_response(status,
nullptr, 0, response, responseSize);
1446vgidsContext* vgids_new(
void)
1449 vgidsContext* ctx = calloc(1,
sizeof(vgidsContext));
1451 ctx->files = ArrayList_New(FALSE);
1454 WLog_ERR(TAG,
"Failed to create files array list");
1458 obj = ArrayList_Object(ctx->files);
1468BOOL vgids_init(vgidsContext* ctx,
const char* cert,
const char* privateKey,
const char* pin)
1471 DWORD keymapSize = 0;
1472 DWORD fsTableSize = 0;
1474 BYTE* kxc =
nullptr;
1475 BYTE* keymap =
nullptr;
1476 BYTE* fsTable =
nullptr;
1477 vgidsEF* masterEF =
nullptr;
1478 vgidsEF* cardidEF =
nullptr;
1479 vgidsEF* commonEF =
nullptr;
1480 BYTE cardid[VGIDS_CARDID_SIZE] = WINPR_C_ARRAY_INIT;
1481 vgidsContainerMapEntry cmrec = { {
'P',
'r',
'i',
'v',
'a',
't',
'e',
' ',
'K',
'e',
'y',
' ',
1483 CONTAINER_MAP_VALID_CONTAINER |
1484 CONTAINER_MAP_DEFAULT_CONTAINER,
1488 vgidsFilesysTableEntry filesys[] = {
1489 {
"mscp",
"", 0, 0, 0, 0xA000, 0 },
1490 {
"",
"cardid", 0, 0xDF20, 0, 0xA012, 0 },
1491 {
"",
"cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1492 {
"",
"cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1493 {
"mscp",
"cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1494 {
"mscp",
"kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1498 if (!cert || !privateKey || !pin)
1500 WLog_DBG(TAG,
"Passed invalid nullptr argument: cert=%p, privateKey=%p, pin=%p",
1501 WINPR_CXX_COMPAT_CAST(
const void*, cert),
1502 WINPR_CXX_COMPAT_CAST(
const void*, privateKey),
1503 WINPR_CXX_COMPAT_CAST(
const void*, pin));
1508 ctx->certificate = freerdp_certificate_new_from_pem(cert);
1509 if (!ctx->certificate)
1512 ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey,
nullptr);
1513 if (!ctx->privateKey)
1518 masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1524 cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1527 if (winpr_RAND(cardid,
sizeof(cardid)) < 0)
1529 if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid,
sizeof(cardid)))
1534 commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1539 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents,
sizeof(g_CardCFContents)))
1544 const size_t size = get_rsa_key_size(ctx->privateKey);
1545 if ((size == 0) || (size > UINT16_MAX / 8))
1548 cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1550 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec,
sizeof(cmrec)))
1554 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1555 sizeof(g_CardAppsContents)))
1559 if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1561 if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1565 if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1567 if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1571 if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1573 if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1577 ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;
1578 ctx->pin = _strdup(pin);
1594BOOL vgids_process_apdu(vgidsContext* context,
const BYTE* data, DWORD dataSize, BYTE** response,
1595 DWORD* responseSize)
1601 if (!context || !data || !response || !responseSize)
1603 WLog_ERR(TAG,
"Invalid nullptr pointer passed");
1609 WLog_ERR(TAG,
"APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1614 Stream_StaticConstInit(&s, data, dataSize);
1619 case ISO_INS_SELECT:
1620 return vgids_ins_select(context, &s, response, responseSize);
1621 case ISO_INS_GETDATA:
1622 return vgids_ins_getdata(context, &s, response, responseSize);
1623 case ISO_INS_GETRESPONSE:
1624 return vgids_ins_getresponse(context, &s, response, responseSize);
1626 return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1628 return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1629 case ISO_INS_VERIFY:
1630 return vgids_ins_verify(context, &s, response, responseSize);
1636 return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED,
nullptr, 0, response, responseSize);
1639void vgids_free(vgidsContext* context)
1643 freerdp_key_free(context->privateKey);
1644 freerdp_certificate_free(context->certificate);
1645 Stream_Free(context->commandData, TRUE);
1646 Stream_Free(context->responseData, TRUE);
1648 ArrayList_Free(context->files);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree