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_KEY_TYPE_KEYEXCHANGE 0x9A
61#define VGIDS_ALGID_RSA_1024 0x06
62#define VGIDS_ALGID_RSA_2048 0x07
63#define VGIDS_ALGID_RSA_3072 0x08
64#define VGIDS_ALGID_RSA_4096 0x09
67#define VGIDS_SE_CRT_SIGN 0xB6
68#define VGIDS_SE_CRT_CONF 0xB8
70#define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40
71#define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80
77#define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40
88#define VGIDS_DEFAULT_KEY_REF 0x81
90#define ISO_INS_SELECT 0xA4
91#define ISO_INS_GETDATA 0xCB
92#define ISO_INS_GETRESPONSE 0xC0
93#define ISO_INS_MSE 0x22
94#define ISO_INS_PSO 0x2A
95#define ISO_INS_VERIFY 0x20
97#define ISO_STATUS_MORE_DATA 0x6100
98#define ISO_STATUS_VERIFYFAILED 0x6300
99#define ISO_STATUS_WRONGLC 0x6700
100#define ISO_STATUS_COMMANDNOTALLOWED 0x6900
101#define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982
102#define ISO_STATUS_AUTHMETHODBLOCKED 0x6983
103#define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80
104#define ISO_STATUS_FILENOTFOUND 0x6A82
105#define ISO_STATUS_INVALIDP1P2 0x6A86
106#define ISO_STATUS_INVALIDLC 0x6A87
107#define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88
108#define ISO_STATUS_SUCCESS 0x9000
110#define ISO_AID_MAX_SIZE 16
112#define ISO_FID_MF 0x3F00
120typedef struct vgids_ef vgidsEF;
128typedef struct vgids_se vgidsSE;
139 rdpCertificate* certificate;
140 rdpPrivateKey* privateKey;
143 SmartcardEmulationContext* emuContext;
147#define VGIDS_MAX_DIGEST_INFO 7
149static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
150 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
151static const BYTE g_PKCS1_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
152 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
153static const BYTE g_PKCS1_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
154 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
155static const BYTE g_PKCS1_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
156 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
157static const BYTE g_PKCS1_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
158 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
159static const BYTE g_PKCS1_SHA512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60,
160 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
161 0x05, 0x05, 0x00, 0x04, 0x1c };
162static const BYTE g_PKCS1_SHA512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
163 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,
164 0x06, 0x05, 0x00, 0x04, 0x20 };
167struct vgids_digest_info_map
171 const EVP_MD* digest;
173typedef struct vgids_digest_info_map vgidsDigestInfoMap;
180static const BYTE g_MsGidsAID[] = {
181 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01
188static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 };
194static const BYTE g_GidsAppFCI[] = { 0x61, 0x12, 0x4F, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42,
195 0x54, 0x46, 0x59, 0x02, 0x01, 0x73, 0x03, 0x40, 0x01, 0xC0 };
205static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 };
208static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 };
218#define MAX_CONTAINER_NAME_LEN 39
224#define CONTAINER_MAP_VALID_CONTAINER 1
229#define CONTAINER_MAP_DEFAULT_CONTAINER 2
231struct vgids_container_map_entry
233 WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1];
236 WORD wSigKeySizeBits;
237 WORD wKeyExchangeKeySizeBits;
239typedef struct vgids_container_map_entry vgidsContainerMapEntry;
241struct vgids_filesys_table_entry
246 UINT16 dataObjectIdentifier;
248 UINT16 fileIdentifier;
251typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry;
253struct vgids_keymap_record
259 UINT16 unknownWithFFFF;
260 UINT16 unknownWith0000;
262typedef struct vgids_keymap_record vgidsKeymapRecord;
266static void vgids_ef_free(
void* ptr);
268WINPR_ATTR_MALLOC(vgids_ef_free, 1)
269static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT
id)
271 vgidsEF* ef = calloc(1,
sizeof(vgidsEF));
274 ef->data = Stream_New(
nullptr, 1024);
277 WLog_ERR(TAG,
"Failed to create file data stream");
280 if (!Stream_SetLength(ef->data, 0))
283 if (!ArrayList_Append(ctx->files, ef))
285 WLog_ERR(TAG,
"Failed to add new ef to file list");
297static BOOL vgids_write_tlv(
wStream* s, UINT16 tag,
const void* data,
size_t dataSize)
299 WINPR_ASSERT(dataSize <= UINT16_MAX);
302 if (!Stream_EnsureRemainingCapacity(s, dataSize + 5ull))
304 WLog_ERR(TAG,
"Failed to ensure capacity of DO stream");
311 Stream_Write_UINT16_BE(s, tag);
313 Stream_Write_UINT8(s, (BYTE)tag);
316 Stream_Write_UINT8(s, (BYTE)dataSize);
318 else if (dataSize < 256)
320 Stream_Write_UINT8(s, 0x81);
321 Stream_Write_UINT8(s, (BYTE)dataSize);
325 Stream_Write_UINT8(s, 0x82);
326 Stream_Write_UINT16_BE(s, (UINT16)dataSize);
328 Stream_Write(s, data, dataSize);
329 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);
341static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)
344 Stream_ResetPosition(ef->data);
347 while (Stream_GetRemainingLength(ef->data) > 3)
354 curPos = Stream_GetPosition(ef->data);
355 Stream_Read_UINT16_BE(ef->data, nextDOID);
356 Stream_Read_UINT8(ef->data, len);
359 BYTE lenSize = len & 0x7F;
360 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))
366 Stream_Read_UINT8(ef->data, doSize);
369 Stream_Read_UINT16_BE(ef->data, doSize);
372 WLog_ERR(TAG,
"Unexpected tag length %" PRIu8, lenSize);
379 if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))
382 if (nextDOID == doID)
384 BYTE* outData =
nullptr;
387 doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);
388 if (!Stream_SetPosition(ef->data, curPos))
391 outData = malloc(doSize);
394 WLog_ERR(TAG,
"Failed to allocate output buffer");
398 Stream_Read(ef->data, outData, doSize);
406 if (!Stream_SafeSeek(ef->data, doSize))
414void vgids_ef_free(
void* ptr)
419 Stream_Free(ef->data, TRUE);
425static BOOL vgids_prepare_fstable(
const vgidsFilesysTableEntry* fstable, DWORD numEntries,
426 BYTE** outData, DWORD* outDataSize)
432 BYTE* data = malloc(
sizeof(vgidsFilesysTableEntry) * numEntries + 1);
435 WLog_ERR(TAG,
"Failed to allocate filesystem table data blob");
440 for (UINT32 i = 0; i < numEntries; ++i)
441 memcpy(data + 1 + (
sizeof(vgidsFilesysTableEntry) * i), &fstable[i],
442 sizeof(vgidsFilesysTableEntry));
445 *outDataSize =
sizeof(vgidsFilesysTableEntry) * numEntries + 1;
451static BOOL vgids_prepare_certificate(
const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)
460 BYTE* comprData =
nullptr;
465 BYTE* certData = freerdp_certificate_get_der(cert, &certSize);
466 if (!certData || (certSize == 0) || (certSize > UINT16_MAX))
468 WLog_ERR(TAG,
"Failed to get certificate size");
472 comprData = malloc(certSize);
475 WLog_ERR(TAG,
"Failed to allocate certificate buffer");
480 destSize = WINPR_ASSERTING_INT_CAST(uint16_t, certSize);
481 if (compress(comprData, &destSize, certData, WINPR_ASSERTING_INT_CAST(uint16_t, certSize)) !=
484 WLog_ERR(TAG,
"Failed to compress certificate data");
489 s = Stream_New(
nullptr, destSize + 4);
490 Stream_Write_UINT16(s, 0x0001);
491 Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, certSize));
492 Stream_Write(s, comprData, destSize);
493 Stream_SealLength(s);
495 *kxc = Stream_Buffer(s);
496 *kxcSize = (DWORD)Stream_Length(s);
498 Stream_Free(s, FALSE);
504 Stream_Free(s, TRUE);
511static size_t get_rsa_key_size(
const rdpPrivateKey* privateKey)
513 WINPR_ASSERT(privateKey);
515 return freerdp_key_get_bits(privateKey) / 8;
519static BYTE vgids_get_algid(vgidsContext* p_Ctx)
523 switch (get_rsa_key_size(p_Ctx->privateKey))
526 return VGIDS_ALGID_RSA_1024;
528 return VGIDS_ALGID_RSA_2048;
530 return VGIDS_ALGID_RSA_3072;
532 return VGIDS_ALGID_RSA_4096;
534 WLog_ERR(TAG,
"Failed to determine algid for private key");
542static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)
548 BYTE* data =
nullptr;
549 vgidsKeymapRecord record = {
552 VGIDS_KEY_TYPE_KEYEXCHANGE,
553 (0xB000 | VGIDS_DEFAULT_KEY_REF),
559 BYTE algid = vgids_get_algid(context);
563 data = malloc(
sizeof(record) + 1);
566 WLog_ERR(TAG,
"Failed to allocate filesystem table data blob");
571 record.algid = algid;
572 memcpy(data + 1, &record,
sizeof(record));
575 *outDataSize =
sizeof(record) + 1;
581static BOOL vgids_parse_apdu_header(
wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,
584 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
589 Stream_Read_UINT8(s, *cla);
593 Stream_Read_UINT8(s, *ins);
597 Stream_Read_UINT8(s, *p1);
601 Stream_Read_UINT8(s, *p2);
608 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
611 Stream_Read_UINT8(s, *lc);
612 if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))
619 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
621 Stream_Read_UINT8(s, *le);
628static BOOL vgids_create_response(UINT16 status,
const BYTE* answer, DWORD answerSize,
629 BYTE** outData, DWORD* outDataSize)
631 BYTE* out = malloc(answerSize + 2);
634 WLog_ERR(TAG,
"Failed to allocate memory for response data");
641 memcpy(out, answer, answerSize);
645 *out = (BYTE)((status >> 8) & 0xFF);
646 *(out + 1) = (BYTE)(status & 0xFF);
647 *outDataSize = answerSize + 2;
652static BOOL vgids_read_do_fkt(
void* data,
size_t index, va_list ap)
654 BYTE* response =
nullptr;
655 DWORD responseSize = 0;
656 vgidsEF* file = (vgidsEF*)data;
657 vgidsContext* context = va_arg(ap, vgidsContext*);
658 UINT16 efID = (UINT16)va_arg(ap,
unsigned);
659 UINT16 doID = (UINT16)va_arg(ap,
unsigned);
662 if (efID == 0x3FFF || efID == file->id)
665 if (vgids_ef_read_do(file, doID, &response, &responseSize))
667 context->responseData = Stream_New(response, (
size_t)responseSize);
668 if (!context->responseData)
678static BOOL vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)
680 return ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);
683static void vgids_reset_context_response(vgidsContext* context)
685 Stream_Free(context->responseData, TRUE);
686 context->responseData =
nullptr;
689static void vgids_reset_context_command_data(vgidsContext* context)
691 Stream_Free(context->commandData, TRUE);
692 context->commandData =
nullptr;
696static BOOL vgids_ins_select(vgidsContext* context,
wStream* s, BYTE** response,
702 DWORD resultDataSize = 0;
703 const BYTE* resultData =
nullptr;
704 UINT16 status = ISO_STATUS_SUCCESS;
708 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
718 BYTE aid[ISO_AID_MAX_SIZE] = WINPR_C_ARRAY_INIT;
719 if (lc > ISO_AID_MAX_SIZE)
721 WLog_ERR(TAG,
"The LC byte is greater than the maximum AID length");
722 status = ISO_STATUS_INVALIDLC;
727 Stream_Read(s, aid, lc);
728 if ((lc >
sizeof(g_MsGidsAID)) || (memcmp(aid, g_MsGidsAID, lc) != 0))
730 status = ISO_STATUS_FILENOTFOUND;
740 resultData = g_GidsAppFCI;
741 resultDataSize =
sizeof(g_GidsAppFCI);
747 resultData = g_GidsAppFCP;
748 resultDataSize =
sizeof(g_GidsAppFCP);
752 status = ISO_STATUS_INVALIDP1P2;
757 context->currentDF = ISO_FID_MF;
767 WLog_ERR(TAG,
"The LC byte for the file ID must be 2");
768 status = ISO_STATUS_INVALIDLC;
772 Stream_Read_UINT16_BE(s, fid);
773 if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)
775 status = ISO_STATUS_FILENOTFOUND;
783 status = ISO_STATUS_INVALIDP1P2;
788 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
792static UINT16 vgids_handle_chained_response(vgidsContext* context,
const BYTE** response,
796 UINT16 status = ISO_STATUS_SUCCESS;
797 DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);
798 if (remainingBytes > 256)
800 status = ISO_STATUS_MORE_DATA;
801 remainingBytes = 256;
804 *response = Stream_Buffer(context->responseData);
805 *responseSize = remainingBytes;
806 Stream_Seek(context->responseData, remainingBytes);
810 remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);
811 if (remainingBytes < 256 && remainingBytes != 0)
812 status |= (remainingBytes & 0xFF);
817static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)
823 WINPR_ASSERT(context);
829 char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);
830 char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);
835 pubKey = Stream_New(
nullptr, nSize + eSize + 0x10);
838 WLog_ERR(TAG,
"Failed to allocate public key stream");
842 response = Stream_New(
nullptr, Stream_Capacity(pubKey) + 0x10);
845 WLog_ERR(TAG,
"Failed to allocate response stream");
850 if (!vgids_write_tlv(pubKey, 0x81, n, nSize))
853 if (!vgids_write_tlv(pubKey, 0x82, e, eSize))
857 if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))
861 Stream_ResetPosition(response);
862 context->responseData = response;
869 Stream_Free(pubKey, TRUE);
870 Stream_Free(response, TRUE);
875static BOOL vgids_ins_getdata(vgidsContext* context,
wStream* s, BYTE** response,
883 DWORD resultDataSize = 0;
884 const BYTE* resultData =
nullptr;
885 UINT16 status = ISO_STATUS_SUCCESS;
891 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
895 vgids_reset_context_response(context);
898 fileId = (UINT16)(((UINT16)p1 << 8) | p2);
907 Stream_Read_UINT8(s, tag);
908 Stream_Read_UINT8(s, length);
909 if (tag != 0x5C && length != 0x02)
911 status = ISO_STATUS_INVALIDCOMMANDDATA;
915 Stream_Read_UINT16_BE(s, doId);
919 (void)vgids_read_do(context, fileId, doId);
930 if (p1 != 0x3F && p2 != 0xFF)
932 status = ISO_STATUS_INVALIDP1P2;
937 Stream_Read_UINT8(s, tag);
938 Stream_Read_UINT8(s, length);
939 if (tag != 0x70 || length != 0x08)
941 status = ISO_STATUS_INVALIDCOMMANDDATA;
946 Stream_Read_UINT8(s, tag);
947 Stream_Read_UINT8(s, length);
948 Stream_Read_UINT8(s, keyRef);
949 if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)
951 status = ISO_STATUS_INVALIDCOMMANDDATA;
956 Stream_Read_UINT8(s, tag);
957 Stream_Read_UINT8(s, length);
958 if (tag != 0xA5 || length != 0x03)
960 status = ISO_STATUS_INVALIDCOMMANDDATA;
964 Stream_Read_UINT16_BE(s, pubKeyDO);
965 Stream_Read_UINT8(s, length);
966 if (pubKeyDO != 0x7F49 || length != 0x80)
968 status = ISO_STATUS_INVALIDCOMMANDDATA;
972 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
974 status = ISO_STATUS_INVALIDLC;
979 if (!vgids_get_public_key(context, pubKeyDO))
984 status = ISO_STATUS_INVALIDCOMMANDDATA;
989 if (context->responseData)
990 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
991 else if (status == ISO_STATUS_SUCCESS)
992 status = ISO_STATUS_REFERENCEDATANOTFOUND;
994 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
998static BOOL vgids_ins_manage_security_environment(vgidsContext* context,
wStream* s,
999 BYTE** response, DWORD* responseSize)
1006 DWORD resultDataSize = 0;
1007 const BYTE* resultData =
nullptr;
1008 UINT16 status = ISO_STATUS_SUCCESS;
1010 vgids_reset_context_command_data(context);
1011 vgids_reset_context_response(context);
1014 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2, &lc,
nullptr))
1020 if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)
1022 status = ISO_STATUS_INVALIDP1P2;
1023 goto create_response;
1028 status = ISO_STATUS_WRONGLC;
1029 goto create_response;
1032 context->currentSE.crt = p2;
1036 Stream_Read_UINT8(s, tag);
1037 Stream_Read_UINT8(s, length);
1038 if (tag != 0x80 || length != 0x01)
1040 status = ISO_STATUS_INVALIDCOMMANDDATA;
1041 goto create_response;
1043 Stream_Read_UINT8(s, context->currentSE.algoId);
1046 Stream_Read_UINT8(s, tag);
1047 Stream_Read_UINT8(s, length);
1048 if (tag != 0x84 || length != 0x01)
1050 status = ISO_STATUS_INVALIDCOMMANDDATA;
1051 goto create_response;
1053 Stream_Read_UINT8(s, context->currentSE.keyRef);
1057 if (status != ISO_STATUS_SUCCESS)
1058 memset(&context->currentSE, 0,
sizeof(context->currentSE));
1059 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1063static BOOL vgids_perform_digital_signature(vgidsContext* context)
1067 EVP_PKEY_CTX* ctx =
nullptr;
1068 EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);
1069 const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = {
1070 { g_PKCS1_SHA1,
sizeof(g_PKCS1_SHA1), EVP_sha1() },
1071 { g_PKCS1_SHA224,
sizeof(g_PKCS1_SHA224), EVP_sha224() },
1072 { g_PKCS1_SHA256,
sizeof(g_PKCS1_SHA256), EVP_sha256() },
1073 { g_PKCS1_SHA384,
sizeof(g_PKCS1_SHA384), EVP_sha384() },
1074 { g_PKCS1_SHA512,
sizeof(g_PKCS1_SHA512), EVP_sha512() },
1075#if OPENSSL_VERSION_NUMBER >= 0x10101000L
1076 { g_PKCS1_SHA512_224,
sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() },
1077 { g_PKCS1_SHA512_256,
sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() }
1083 WLog_ERR(TAG,
"Failed to create PKEY");
1087 vgids_reset_context_response(context);
1090 Stream_ResetPosition(context->commandData);
1091 for (
int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)
1094 const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];
1095 if (Stream_Length(context->commandData) >= digest->infoSize &&
1096 memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)
1099 Stream_Seek(context->commandData, digest->infoSize);
1100 if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))
1102 msgSize = Stream_GetRemainingLength(context->commandData);
1105 ctx = EVP_PKEY_CTX_new(pk,
nullptr);
1108 WLog_ERR(TAG,
"Failed to create signing context");
1112 if (EVP_PKEY_sign_init(ctx) <= 0)
1114 WLog_ERR(TAG,
"Failed to init signing context");
1119 if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)
1121 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
1123 WLog_ERR(TAG,
"Failed to set padding mode");
1128 if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)
1130 WLog_ERR(TAG,
"Failed to set signing mode");
1135 if (EVP_PKEY_sign(ctx,
nullptr, &sigSize, Stream_Pointer(context->commandData),
1138 WLog_ERR(TAG,
"Failed to determine signature size");
1142 context->responseData = Stream_New(
nullptr, sigSize);
1143 if (!context->responseData)
1145 WLog_ERR(TAG,
"Failed to allocate signing buffer");
1150 if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,
1151 Stream_Pointer(context->commandData), msgSize) <= 0)
1153 WLog_ERR(TAG,
"Failed to create signature");
1157 if (!Stream_SetLength(context->responseData, sigSize))
1160 EVP_PKEY_CTX_free(ctx);
1166 vgids_reset_context_command_data(context);
1170 vgids_reset_context_command_data(context);
1171 vgids_reset_context_response(context);
1172 EVP_PKEY_CTX_free(ctx);
1178static BOOL vgids_perform_decrypt(vgidsContext* context)
1180 EVP_PKEY_CTX* ctx =
nullptr;
1183 int padding = RSA_NO_PADDING;
1185 vgids_reset_context_response(context);
1188 if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)
1189 padding = RSA_PKCS1_PADDING;
1190 else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)
1191 padding = RSA_PKCS1_OAEP_PADDING;
1194 EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);
1196 goto decrypt_failed;
1197 ctx = EVP_PKEY_CTX_new(pkey,
nullptr);
1199 goto decrypt_failed;
1200 if (EVP_PKEY_decrypt_init(ctx) <= 0)
1201 goto decrypt_failed;
1202 if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
1203 goto decrypt_failed;
1207 const size_t inlen = Stream_Length(context->commandData);
1209 res = EVP_PKEY_decrypt(ctx,
nullptr, &outlen, Stream_Buffer(context->commandData), inlen);
1212 WLog_ERR(TAG,
"Failed to decrypt data");
1213 goto decrypt_failed;
1217 context->responseData = Stream_New(
nullptr, outlen);
1219 if (!context->responseData)
1221 WLog_ERR(TAG,
"Failed to create decryption buffer");
1222 goto decrypt_failed;
1226 res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,
1227 Stream_Buffer(context->commandData), inlen);
1231 WLog_ERR(TAG,
"Failed to decrypt data");
1232 goto decrypt_failed;
1235 rc = Stream_SetLength(context->responseData, outlen);
1239 EVP_PKEY_CTX_free(ctx);
1240 EVP_PKEY_free(pkey);
1241 vgids_reset_context_command_data(context);
1243 vgids_reset_context_response(context);
1248static BOOL vgids_ins_perform_security_operation(vgidsContext* context,
wStream* s, BYTE** response,
1249 DWORD* responseSize)
1255 DWORD resultDataSize = 0;
1256 const BYTE* resultData =
nullptr;
1257 UINT16 status = ISO_STATUS_SUCCESS;
1260 if (!vgids_parse_apdu_header(s, &cla,
nullptr, &p1, &p2, &lc,
nullptr))
1265 status = ISO_STATUS_WRONGLC;
1266 goto create_response;
1270 if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)
1272 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1273 goto create_response;
1277 if (!context->pinVerified)
1279 status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;
1280 goto create_response;
1284 if (!context->commandData)
1286 context->commandData = Stream_New(
nullptr, lc);
1287 if (!context->commandData)
1290 else if (!Stream_EnsureRemainingCapacity(context->commandData, lc))
1293 Stream_Write(context->commandData, Stream_Pointer(s), lc);
1294 Stream_SealLength(context->commandData);
1297 switch (context->currentSE.crt)
1299 case VGIDS_SE_CRT_SIGN:
1301 if (p1 != 0x9E || p2 != 0x9A)
1303 status = ISO_STATUS_INVALIDP1P2;
1310 if (!vgids_perform_digital_signature(context))
1315 case VGIDS_SE_CRT_CONF:
1317 if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))
1319 status = ISO_STATUS_INVALIDP1P2;
1326 if (!vgids_perform_decrypt(context))
1332 status = ISO_STATUS_INVALIDP1P2;
1337 if (status == ISO_STATUS_SUCCESS && context->responseData)
1338 status = vgids_handle_chained_response(context, &resultData, &resultDataSize);
1342 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1346static BOOL vgids_ins_getresponse(vgidsContext* context,
wStream* s, BYTE** response,
1347 DWORD* responseSize)
1352 DWORD resultDataSize = 0;
1353 const BYTE* resultData =
nullptr;
1354 DWORD expectedLen = 0;
1355 DWORD remainingSize = 0;
1356 UINT16 status = ISO_STATUS_SUCCESS;
1360 if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))
1362 status = ISO_STATUS_COMMANDNOTALLOWED;
1363 goto create_response;
1366 if (!vgids_parse_apdu_header(s,
nullptr,
nullptr, &p1, &p2,
nullptr, &le))
1370 if (p1 != 00 || p2 != 0x00)
1372 status = ISO_STATUS_INVALIDP1P2;
1373 goto create_response;
1378 if (expectedLen == 0)
1382 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1383 if (remainingSize < expectedLen)
1384 expectedLen = remainingSize;
1386 resultData = Stream_Pointer(context->responseData);
1387 resultDataSize = expectedLen;
1388 Stream_Seek(context->responseData, expectedLen);
1391 remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);
1392 if (remainingSize > 0)
1394 status = ISO_STATUS_MORE_DATA;
1395 if (remainingSize < 256)
1396 status |= (remainingSize & 0xFF);
1400 return vgids_create_response(status, resultData, resultDataSize, response, responseSize);
1404static BOOL vgids_ins_verify(vgidsContext* context,
wStream* s, BYTE** response,
1405 DWORD* responseSize)
1411 UINT16 status = ISO_STATUS_SUCCESS;
1412 char pin[VGIDS_MAX_PIN_SIZE + 1] = WINPR_C_ARRAY_INIT;
1415 if (!vgids_parse_apdu_header(s,
nullptr, &ins, &p1, &p2,
nullptr,
nullptr))
1419 if (p1 != 00 && p2 != 0x80 && p2 != 0x82)
1421 status = ISO_STATUS_INVALIDP1P2;
1422 goto create_response;
1428 context->pinVerified = FALSE;
1429 goto create_response;
1433 if (Emulate_IsPinBlocked(context->emuContext, context->name))
1435 status = ISO_STATUS_AUTHMETHODBLOCKED;
1436 goto create_response;
1440 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1442 status = ISO_STATUS_INVALIDLC;
1443 goto create_response;
1446 Stream_Read_UINT8(s, lc);
1447 if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))
1449 status = ISO_STATUS_INVALIDLC;
1450 goto create_response;
1454 Stream_Read(s, pin, lc);
1456 UINT16 remaining = 0;
1457 if (!Emulate_IsPinValid(context->emuContext, context->name, pin, lc, &remaining))
1460 context->pinVerified = FALSE;
1461 status = (ISO_STATUS_VERIFYFAILED | (remaining & 0xFF));
1466 context->pinVerified = TRUE;
1470 return vgids_create_response(status,
nullptr, 0, response, responseSize);
1473vgidsContext* vgids_new(SmartcardEmulationContext* context)
1478 vgidsContext* ctx = calloc(1,
sizeof(vgidsContext));
1481 ctx->emuContext = context;
1483 ctx->files = ArrayList_New(FALSE);
1486 WLog_ERR(TAG,
"Failed to create files array list");
1490 wObject* obj = ArrayList_Object(ctx->files);
1500BOOL vgids_init(vgidsContext* ctx,
const char* cert,
const char* privateKey,
const char* pin)
1503 DWORD keymapSize = 0;
1504 DWORD fsTableSize = 0;
1506 BYTE* kxc =
nullptr;
1507 BYTE* keymap =
nullptr;
1508 BYTE* fsTable =
nullptr;
1509 vgidsEF* masterEF =
nullptr;
1510 vgidsEF* cardidEF =
nullptr;
1511 vgidsEF* commonEF =
nullptr;
1512 BYTE cardid[VGIDS_CARDID_SIZE] = WINPR_C_ARRAY_INIT;
1513 vgidsContainerMapEntry cmrec = { {
'P',
'r',
'i',
'v',
'a',
't',
'e',
' ',
'K',
'e',
'y',
' ',
1515 CONTAINER_MAP_VALID_CONTAINER |
1516 CONTAINER_MAP_DEFAULT_CONTAINER,
1520 vgidsFilesysTableEntry filesys[] = {
1521 {
"mscp",
"", 0, 0, 0, 0xA000, 0 },
1522 {
"",
"cardid", 0, 0xDF20, 0, 0xA012, 0 },
1523 {
"",
"cardapps", 0, 0xDF21, 0, 0xA010, 0 },
1524 {
"",
"cardcf", 0, 0xDF22, 0, 0xA010, 0 },
1525 {
"mscp",
"cmapfile", 0, 0xDF23, 0, 0xA010, 0 },
1526 {
"mscp",
"kxc00", 0, 0xDF24, 0, 0xA010, 0 },
1530 if (!cert || !privateKey || !pin)
1532 WLog_DBG(TAG,
"Passed invalid nullptr argument: cert=%p, privateKey=%p, pin=%p",
1533 WINPR_CXX_COMPAT_CAST(
const void*, cert),
1534 WINPR_CXX_COMPAT_CAST(
const void*, privateKey),
1535 WINPR_CXX_COMPAT_CAST(
const void*, pin));
1540 ctx->certificate = freerdp_certificate_new_from_pem(cert);
1541 if (!ctx->certificate)
1544 ctx->privateKey = freerdp_key_new_from_pem_enc(privateKey,
nullptr);
1545 if (!ctx->privateKey)
1548 ctx->name = freerdp_certificate_get_fingerprint(ctx->certificate);
1554 masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);
1560 cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);
1563 if (winpr_RAND(cardid,
sizeof(cardid)) < 0)
1565 if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid,
sizeof(cardid)))
1570 commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);
1575 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents,
sizeof(g_CardCFContents)))
1580 const size_t size = get_rsa_key_size(ctx->privateKey);
1581 if ((size == 0) || (size > UINT16_MAX / 8))
1584 cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;
1586 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec,
sizeof(cmrec)))
1590 if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,
1591 sizeof(g_CardAppsContents)))
1595 if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))
1597 if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))
1601 if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))
1603 if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))
1607 if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))
1609 if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))
1613 if (!Emulate_SetupPin(ctx->emuContext, ctx->name, pin))
1628BOOL vgids_process_apdu(vgidsContext* context,
const BYTE* data, DWORD dataSize, BYTE** response,
1629 DWORD* responseSize)
1635 if (!context || !data || !response || !responseSize)
1637 WLog_ERR(TAG,
"Invalid nullptr pointer passed");
1643 WLog_ERR(TAG,
"APDU buffer is less than 4 bytes: %" PRIu32, dataSize);
1648 Stream_StaticConstInit(&s, data, dataSize);
1653 case ISO_INS_SELECT:
1654 return vgids_ins_select(context, &s, response, responseSize);
1655 case ISO_INS_GETDATA:
1656 return vgids_ins_getdata(context, &s, response, responseSize);
1657 case ISO_INS_GETRESPONSE:
1658 return vgids_ins_getresponse(context, &s, response, responseSize);
1660 return vgids_ins_manage_security_environment(context, &s, response, responseSize);
1662 return vgids_ins_perform_security_operation(context, &s, response, responseSize);
1663 case ISO_INS_VERIFY:
1664 return vgids_ins_verify(context, &s, response, responseSize);
1670 return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED,
nullptr, 0, response, responseSize);
1673void vgids_free(vgidsContext* context)
1677 freerdp_key_free(context->privateKey);
1678 freerdp_certificate_free(context->certificate);
1679 Stream_Free(context->commandData, TRUE);
1680 Stream_Free(context->responseData, TRUE);
1681 winpr_zfree(context->name);
1682 ArrayList_Free(context->files);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree