SCardTransmit
245 行 SCardTransmit 定义在 winscard_clnt.c
实现如下:
| 2915 LONG SCardTransmit(SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, 2916 LPCBYTE pbSendBuffer, DWORD cbSendLength, 2917 LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer,
2918 LPDWORD pcbRecvLength) 2919 { |
问:为什么从代码解说开始,每个函数 API 都不说明参数和用法?
答案是, 这些 API 都是符合 PCSC 规范的。第一章的各卷部分都对应做了说明了。所以不再重复。或者也可以看看源代码的这些函数实现的注释部分,这些参数都有注释说明。注释格式符合 Doxygen 要求。
如果这里重复说明,也就是重复昨天的故事,很罗曼蒂克。一可以增加篇幅,二可以增加工作量,三可以倍显所谓的耐心和专业精神。但此时此地,还是不要。
| 2920 LONG rv; 2921 int i; 2922 DWORD dwContextIndex, dwChannelIndex; 2923 2924 PROFILE_START 2925 2926 if (pbSendBuffer == NULL || pbRecvBuffer == NULL || 2927 pcbRecvLength == NULL || pioSendPci == NULL) 2928 return SCARD_E_INVALID_PARAMETER; 2929 |
2920~2929 常规检查。
问:上面提到了很多 API 了,实现的前部都有检查。能不能不检查?
答案是,在 c 的世界里,不检查意味着崩溃。意味着用彻夜的调试来消耗生命。不过
目前看,生命没有房价值钱,是吧?在 metaprogram 中可以实现这个想法。如果不接受
检查,那么赶快投入 metaprogram 的怀抱,把运行期的错误转移到编译期。
“ 把一个人的温暖转移到另一个人的胸膛 ” 。
| 2934 /* 2935 * Make sure this handle has been opened 2936 */ 2937 rv = SCardGetIndicesFromHandle(hCard, &dwContextIndex, &dwChannelIndex); 2938 if (rv == -1) 2939 { 2940 *pcbRecvLength = 0; 2941 return SCARD_E_INVALID_HANDLE; 2942 } 2943 2944 (void)SYS_MutexLock(psContextMap[dwContextIndex].mMutex); 2945 2946 /* check the handle is still valid */
2947 rv = SCardGetIndicesFromHandle(hCard, &dwContextIndex, &dwChannelIndex); 2948 if (rv == -1) 2949 /* the handle is now invalid 2950 * -> another thread may have called SCardReleaseContext 2951 * -> so the mMutex has been unlocked */ 2952 return SCARD_E_INVALID_HANDLE; 2953 |
2934~2953 行,前面讲过了?似乎?是讲解过类似的片段,讲了 n 次。
但这个片段不太一样。
找出以前的片段对比对比。有对比才有发现,有攀比才有仇富,有发现才会警醒。
| 1872 LONG SCardGetStatusChange(SCARDCONTEXT hContext, DWORD dwTimeout, 1873 LPSCARD_READERSTATE_A rgReaderStates, DWORD cReaders) |
...
| 1918 dwContextIndex = SCardGetContextIndice(hContext); 1919 if (dwContextIndex == -1) 1920 return SCARD_E_INVALID_HANDLE; 1921 1922 (void)SYS_MutexLock(psContextMap[dwContextIndex].mMutex); 1923 1924 /* check the context is still opened */ 1925 dwContextIndex = SCardGetContextIndice(hContext); 1926 if (dwContextIndex == -1) 1927 /* the context is now invalid 1928 * -> another thread may have called SCardReleaseContext 1929 * -> so the mMutex has been unlocked */ 1930 return SCARD_E_INVALID_HANDLE; |
重要差别在
一个是
| 2937 rv = SCardGetIndicesFromHandle(hCard, &dwContextIndex, &dwChannelIndex); |
另一个是
| 1925 dwContextIndex = SCardGetContextIndice(hContext); |
SCardGetContextIndice 上面讲解过了,从 APPLICATION 大佬的 psContextMap 数组
那里获得索引号。
SCardGetIndicesFromHandle 做什么呢?
| 3740 static LONG SCardGetIndicesFromHandle(SCARDHANDLE hCard, 3741 PDWORD pdwContextIndice, PDWORD pdwChannelIndice) 3742 { 3743 LONG rv; 3744 3745 if (0 == hCard) 3746 return -1; 3747 3748 (void)SCardLockThread(); 3749 rv = SCardGetIndicesFromHandleTH(hCard, pdwContextIndice, pdwChannelIndice); 3750 (void)SCardUnlockThread(); 3751 3752 return rv; 3753 } |
SCardGetIndicesFromHandle 把 SCardGetIndicesFromHandleTH 加锁并做 wrapper.
还是看看 SCardGetIndicesFromHandleTH.
| 3755 static LONG SCardGetIndicesFromHandleTH(SCARDHANDLE hCard, 3756 PDWORD pdwContextIndice, PDWORD pdwChannelIndice) 3757 { 3758 int i; 3759 3760 for (i = 0; i < PCSCLITE_MAX_APPLICATION_CONTEXTS; i++) 3761 { 3762 if (psContextMap[i].hContext != 0) 3763 { 3764 int j; 3765 3766 for (j = 0; j < PCSCLITE_MAX_APPLICATION_CONTEXT_CHANNELS; j++) 3767 { 3768 if (psContextMap[i].psChannelMap[j].hCard == hCard) 3769 { 3770 *pdwContextIndice = i;
3771 *pdwChannelIndice = j; 3772 return SCARD_S_SUCCESS; 3773 } 3774 } 3775 3776 } 3777 } 3778 3779 return -1; 3780 } |
3755~3780 行,从 psContextMap 大佬那里获知 ContextIndice 和 ChannelIndice.
后面要用到 ContextIndice 从 psContextMap[ContextIndice] 获得应用上下文。
而上下文中维护的读卡器的名字就存在
psContextMap[dwContextIndex].psChannelMap[dwChannelIndex].readerName
而这个读卡器名字的信息什么时候被维护到上下文中?
在 SCardAddHandle 的时候,而 SCardAddHanle 在 SCardConnect 中。
明白了吧。
特别说明, psChannelMap 里的每个元素用来代表同一个 psContextMap 元素上下文中,
的一个逻辑连接 (SCardConnect).
回头看看
| 214 rv = SCardConnect(hContext, &mszReaders[iList[iReader]], 215 SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, 216 &hCard, &dwPref); |
SCardTransmit 的第一个参数 hCard 在 SCardConnect 的时候建立的。
所以可以推理,只要用到和卡通讯的 API, 比如
SCardTransmit
SCardControl
SCardGetAttrib
SCardStatus
SCardReconnect
第一个参数都是 hCard. 而不需要和卡交换数据的 API, 第一个参数是 hContext.
回到 SCardTransmit
| 2954 for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++) 2955 { 2956 char *r = psContextMap[dwContextIndex].psChannelMap[dwChannelIndex].readerName;
2957 2958 /* by default r == NULL */ 2959 if (r && strcmp(r, (readerStates[i])->readerName) == 0) 2960 break; 2961 } 2962 2963 if (i == PCSCLITE_MAX_READERS_CONTEXTS) 2964 { 2965 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 2966 return SCARD_E_READER_UNAVAILABLE; 2967 } |
2954~2961 行,前面的 SCardGetIndicesFromHandlez 之后已经获得了 dwChannelIndex,
dwContextIndex. 接下来与共享内存 readerStates 逐个进行比较,若没有匹配,则退出。
为什么会没有匹配?因为读卡器可能又被拔出。
| 2969 if ((cbSendLength > MAX_BUFFER_SIZE_EXTENDED) 2970 || (*pcbRecvLength > MAX_BUFFER_SIZE_EXTENDED)) 2971 { 2972 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 2973 return SCARD_E_INSUFFICIENT_BUFFER; 2974 } |
以下是两个最大值的定义
| #define MAX_BUFFER_SIZE 264 /**< Maximum Tx/Rx Buffer for short APDU */
# define MAX_BUFFER_SIZE_EXTENDED (4 + 3 + (1<<16) + 3) /**< enhanced (64K + APDU + Lc + Le) Tx/Rx Buffer */ |
这样就知道了
2969~2974 的存在原因了。
| 2975 2976 if ((cbSendLength > MAX_BUFFER_SIZE) || (*pcbRecvLength > MAX_BUFFER_SIZE))
2977 { 2978 /* extended APDU */ 2979 unsigned char buffer[sizeof(sharedSegmentMsg) + MAX_BUFFER_SIZE_EXTENDED]; 2980 transmit_struct_extended *scTransmitStructExtended = (transmit_struct_extended *)buffer; 2981 sharedSegmentMsg *pmsgStruct = (psharedSegmentMsg)buffer; 2982 2983 scTransmitStructExtended->hCard = hCard; 2984 scTransmitStructExtended->cbSendLength = cbSendLength; 2985 scTransmitStructExtended->pcbRecvLength = *pcbRecvLength; 2986 /* The size of data to send is the size of 2987 * struct control_struct_extended WITHOUT the data[] field 2988 * plus the effective data[] size 2989 */ 2990 scTransmitStructExtended->size = sizeof(*scTransmitStructExtended) 2991 - (sizeof(transmit_struct_extended) - offsetof(transmit_struct_extended, data)) 2992 + cbSendLength; 2993 scTransmitStructExtended->ioSendPciProtocol = pioSendPci->dwProtocol; 2994 scTransmitStructExtended->ioSendPciLength = pioSendPci->cbPciLength; 2995 memcpy(scTransmitStructExtended->data, pbSendBuffer, cbSendLength); 2996 scTransmitStructExtended->rv = SCARD_S_SUCCESS; 2997 |
2979 行, sharedSegmentMsg 的结构如下,在 winscard_msg.h
| typedef struct rxSharedSegment { uint32_t mtype; /** one of the /c pcsc_adm_commands */ uint32_t user_id; uint32_t group_id; uint32_t command; /** one of the /c pcsc_msg_commands */ uint64_t date; unsigned char key[PCSCLITE_MSG_KEY_LEN]; /* 16 bytes */ union {
unsigned char data[PCSCLITE_MAX_MESSAGE_SIZE];
struct version_struct veStr; }; } sharedSegmentMsg, *psharedSegmentMsg; #define PCSCLITE_MAX_MESSAGE_SIZE 2048 |
transmit_struct_extended 的结构如下,在 winscard_msg.h
| struct transmit_struct_extended { int32_t hCard; uint32_t ioSendPciProtocol; uint32_t ioSendPciLength; uint32_t cbSendLength; uint32_t ioRecvPciProtocol; uint32_t ioRecvPciLength; uint32_t pcbRecvLength; uint32_t rv; uint64_t size; uint8_t data[1]; };
typedef struct transmit_struct_extended transmit_struct_extended; |
2990~2991 行,
| 2990 scTransmitStructExtended->size = sizeof(*scTransmitStructExtended) 2991 - (sizeof(transmit_struct_extended) - offsetof(transmit_struct_extended, data))
2986 /* The size of data to send is the size of 2987 * struct control_struct_extended WITHOUT the data[] field 2988 * plus the effective data[] size 2989 */ |
这些行的解释足够说明上面的 size 的意义。
特别注意 offsetof ,查下 man 手册,
打开终端,
#man offsetof
#include <stddef.h>
size_t offsetof(type, member);
不用看文档,直接告诉您,
结构体成员 member 相对于 type 结构体基地址的偏移量。
当然了,也许您早就知道了。多言了。
2977~2997 行,初始化
scTransmitStructExtended 的各个成员变量。
| 2998 if (pioRecvPci) 2999 { 3000 scTransmitStructExtended->ioRecvPciProtocol = pioRecvPci->dwProtocol; 3001 scTransmitStructExtended->ioRecvPciLength = pioRecvPci->cbPciLength; 3002 } 3003 else 3004 { 3005 scTransmitStructExtended->ioRecvPciProtocol = SCARD_PROTOCOL_ANY; 3006 scTransmitStructExtended->ioRecvPciLength = sizeof(SCARD_IO_REQUEST); 3007 } |
2998~3007 行,初始化接收使用的协议。
SCARD_IO_REQUEST 定义如下,在 PCSC/pcsclite.h
| /** Protocol Control Information (PCI) */ typedef struct { unsigned long dwProtocol; /**< Protocol identifier */ unsigned long cbPciLength; /**< Protocol Control Inf Length */ } SCardTransmit SCARD_IO_REQUEST, *PSCARD_IO_REQUEST, *LPSCARD_IO_REQUEST; |
| 3008 3009 rv = WrapSHMWrite(SCARD_TRANSMIT_EXTENDED, 3010 psContextMap[dwContextIndex].dwClientID, 3011 scTransmitStructExtended->size, 3012 PCSCLITE_CLIENT_ATTEMPTS, buffer); 3013 3014 if (rv == -1) 3015 { 3016 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex);
3017 return SCARD_E_NO_SERVICE; 3018 } 3019 3020 /* 3021 * Read a message from the server 3022 */ 3023 /* read the first block */ 3024 rv = SHMMessageReceive(buffer, sizeof(sharedSegmentMsg), psContextMap[dwContextIndex].dwCl ientID, PCSCLITE_CLIENT_ATTEMPTS); 3025 if (rv == -1) 3026 { 3027 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3028 return SCARD_F_COMM_ERROR; 3029 } |
3009 行, 3024 行, WrapSHMWrite 和 SHMMessageReceive 前面都解释过了。
需要注意的是,发送的数据格式是 transmit_struct_extended ,而接收第一个块的数据格式是 sharedSegmentMsg.
3024 行首先接收一个大小为 sharedSegmentMsg 的数据包。 data 域才是 transmit_struct_extended 结构。
再看看这个定义
| typedef struct rxSharedSegment { uint32_t mtype; /** one of the /c pcsc_adm_commands */ uint32_t user_id; uint32_t group_id; uint32_t command; /** one of the /c pcsc_msg_commands */ uint64_t date; unsigned char key[PCSCLITE_MSG_KEY_LEN]; /* 16 bytes */ union { unsigned char data[PCSCLITE_MAX_MESSAGE_SIZE]; struct version_struct veStr; }; } sharedSegmentMsg, *psharedSegmentMsg; |
把返回的 transmit_struct_extended 存在 sharedSegmentMsg 的 data 字段。
上面分析过了,发送的时候 transmit_struct_extended 的 size 成员,表示了
从 transmit_struct_extended
的第一个成员 hCard 到 size 的长度包括 size ,再加上实际的
发送数据长度。在接收的时候,也一样。 transmit_struct_extended 的 size 成员,表示了
从 transmit_struct_extended
的第一个成员 hCard 到 size 的长度包括 size 本身所占的大小,
再加上实际的接收数据长度。
| 3030 3031 /* we receive a sharedSegmentMsg and not a transmit_struct_extended */ 3032 scTransmitStructExtended = (transmit_struct_extended *)&(pmsgStruct -> data); 3033 3034 /* a second block is present */ 3035 if (scTransmitStructExtended->size > PCSCLITE_MAX_MESSAGE_SIZE) 3036 { 3037 rv = SHMMessageReceive(buffer + sizeof(sharedSegmentMsg), 3038 scTransmitStructExtended->size-PCSCLITE_MAX_MESSAGE_SIZE, 3039 psContextMap[dwContextIndex].dwClientID, 3040 PCSCLITE_CLIENT_ATTEMPTS); 3041 if (rv == -1) 3042 { 3043 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3044 return SCARD_F_COMM_ERROR; 3045 } 3046 } |
3035 行,如果比较成立,证明还有一个数据块需要接收。因为 sharedSegmentMsg
的 data 成员长度为 PCSCLITE_MAX_MESSAGE_SIZE 。
3036~3046 行,经过上面的解释,很好理解了。就是再接收第二个块。
| 3047 3048 if (scTransmitStructExtended -> rv == SCARD_S_SUCCESS) 3049 { 3050 /* 3051 * Copy and zero it so any secret information is not leaked
3052 */ 3053 memcpy(pbRecvBuffer, scTransmitStructExtended -> data, 3054 scTransmitStructExtended -> pcbRecvLength); 3055 memset(scTransmitStructExtended -> data, 0x00, 3056 scTransmitStructExtended -> pcbRecvLength); 3057 3058 if (pioRecvPci) 3059 { 3060 pioRecvPci->dwProtocol = scTransmitStructExtended->ioRecvPciProtocol; 3061 pioRecvPci->cbPciLength = scTransmitStructExtended->ioRecvPciLength; 3062 } 3063 } 3064 3065 *pcbRecvLength = scTransmitStructExtended -> pcbRecvLength; 3066 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3067 3068 rv = scTransmitStructExtended -> rv; 3069 } |
3047~3069 行,把实际接收的数据,实际接收的数据长度,接收时使用的协议类型,通讯的结果 rv
复制给函数入口处对应的参数。
到了这里,长 APDU 的 transmit 解说结束。
当然了,下面的 else 分支就开始了短 APDU 的发送接收了。
上面提到的长 APDU 用到的那两种结构。发送采用 transmit_struct_extended
结构,接收采用 sharedSegmentMsg 结构, data 域内嵌 transmit_struct_extended 结构。
相比,短 APDU 也用到了两种结构。发送采用 transmit_struct 结构,接收采用 sharedSegmentMsg 结构, data 域内嵌 transmit_struct 。
transmit_struct 的结构如下,在 winscard_msg.h
| struct transmit_struct { int32_t hCard; uint32_t ioSendPciProtocol; uint32_t ioSendPciLength; uint8_t pbSendBuffer[MAX_BUFFER_SIZE]; uint32_t cbSendLength;
uint32_t ioRecvPciProtocol; uint32_t ioRecvPciLength; uint8_t pbRecvBuffer[MAX_BUFFER_SIZE]; uint32_t pcbRecvLength; uint32_t rv; }; |
| 3070 else 3071 { 3072 /* short APDU */ 3073 transmit_struct scTransmitStruct; 3074 sharedSegmentMsg msgStruct; 3075 3076 scTransmitStruct.hCard = hCard; 3077 scTransmitStruct.cbSendLength = cbSendLength; 3078 scTransmitStruct.pcbRecvLength = *pcbRecvLength; 3079 scTransmitStruct.ioSendPciProtocol = pioSendPci->dwProtocol; 3080 scTransmitStruct.ioSendPciLength = pioSendPci->cbPciLength; 3081 memcpy(scTransmitStruct.pbSendBuffer, pbSendBuffer, cbSendLength); 3082 memset(scTransmitStruct.pbSendBuffer+cbSendLength, 0, sizeof(scTransmitStruct.pbSendBuffer )-cbSendLength); 3083 memset(scTransmitStruct.pbRecvBuffer, 0, sizeof(scTransmitStruct.pbRecvBuffer)); 3084 scTransmitStruct.rv = SCARD_S_SUCCESS; |
3070~3084 行,初始化 scTransmitStruct 各个成员,
3082 行,还对 transmit_struct
结构中,用剩的 pbSendBuffer 进行 0 填充。
3083 行,还对接收缓冲区 pbRecvBuffer 清 0.
长 APDU 的处理,没有进行这些。实际上,可以用也可以不用这两步。
| 3085 3086 if (pioRecvPci) 3087 { 3088 scTransmitStruct.ioRecvPciProtocol = pioRecvPci->dwProtocol; 3089 scTransmitStruct.ioRecvPciLength = pioRecvPci->cbPciLength; 3090 } 3091 else 3092 { 3093 scTransmitStruct.ioRecvPciProtocol = SCARD_PROTOCOL_ANY; 3094 scTransmitStruct.ioRecvPciLength = sizeof(SCARD_IO_REQUEST);
3095 } |
3085~3095 行,和长 APDU 的收发处理是一样的。
| 3096 3097 rv = WrapSHMWrite(SCARD_TRANSMIT, 3098 psContextMap[dwContextIndex].dwClientID, sizeof(scTransmitStruct), 3099 PCSCLITE_CLIENT_ATTEMPTS, (void *) &scTransmitStruct); 3100 3101 if (rv == -1) 3102 { 3103 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3104 return SCARD_E_NO_SERVICE; 3105 } 3106 3107 /* 3108 * Read a message from the server 3109 */ 3110 rv = SHMClientRead(&msgStruct, psContextMap[dwContextIndex].dwClientID, 3111 PCSCLITE_CLIENT_ATTEMPTS); 3112 3113 memcpy(&scTransmitStruct, &msgStruct.data, sizeof(scTransmitStruct)); 3114 3115 if (rv == -1) 3116 { 3117 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3118 return SCARD_F_COMM_ERROR; 3119 } |
3096~3119 行,发,然后收,从 msgStruct.data 中,复制出内嵌的 scTransmitStruct 结构。
| 3120 3121 /* 3122 * Zero it and free it so any secret information cannot be leaked 3123 */
3124 memset(scTransmitStruct.pbSendBuffer, 0x00, cbSendLength); 3125 3126 if (scTransmitStruct.rv == SCARD_S_SUCCESS) 3127 { 3128 /* 3129 * Copy and zero it so any secret information is not leaked 3130 */ 3131 memcpy(pbRecvBuffer, scTransmitStruct.pbRecvBuffer, 3132 scTransmitStruct.pcbRecvLength); 3133 memset(scTransmitStruct.pbRecvBuffer, 0x00, 3134 scTransmitStruct.pcbRecvLength); 3135 3136 if (pioRecvPci) 3137 { 3138 pioRecvPci->dwProtocol = scTransmitStruct.ioRecvPciProtocol; 3139 pioRecvPci->cbPciLength = scTransmitStruct.ioRecvPciLength; 3140 } 3141 } 3142 3143 *pcbRecvLength = scTransmitStruct.pcbRecvLength; 3144 (void)SYS_MutexUnLock(psContextMap[dwContextIndex].mMutex); 3145 3146 rv = scTransmitStruct.rv; 3147 } 3148 3149 PROFILE_END(rv) 3150 3151 return rv; 3152 } |
3120~3147 行,把实际接收的数据,实际接收的数据长度,接收时使用的协议类型,通讯的结果 rv 复制给函数入口处对应的参数。
SCardTransmit 解说结束。
回头,回头,回到 testpcsc.c
| 247 test_rv(rv, hContext, PANIC); 248 printf(" card response:" GREEN); 249 for (i=0; i<length; i++)
250 printf(" %02X", bRecvBuffer[i]); 251 printf("/n" NORMAL ); 252 |
247~252 行把在进行 MF 选择后,接收的报文打印出。
| 253 printf("Testing SCardControl/t/t: "); 254 #ifdef PCSC_PRE_120 255 { 256 char buffer[1024] = "Foobar"; 257 DWORD cbRecvLength = sizeof(buffer); 258 259 rv = SCardControl(hCard, buffer, 7, buffer, &cbRecvLength); 260 } 261 #else 262 { 263 char buffer[1024] = { 0x02 }; 264 DWORD cbRecvLength = sizeof(buffer); 265 266 rv = SCardControl(hCard, SCARD_CTL_CODE(1), buffer, 1, buffer, 267 sizeof(buffer), &cbRecvLength); 268 if (cbRecvLength && (SCARD_S_SUCCESS == rv)) 269 { 270 for (i=0; i<cbRecvLength; i++) 271 printf("%c", buffer[i]); 272 printf(" "); 273 } 274 } 275 #endif |
253~275 行进行 SCardControl 测试。
可以使用 SCardControl ,进行一些产商特殊控制。
控制读卡器上的一些特殊功能,比如和读卡器集成一体的键盘操作,生物识别等。
SCardTransmit函数代码解析与相关探讨
本文详细解析了PCSC的SCardTransmit函数实现,涉及参数检查、共享内存处理、APDU的发送与接收,以及在不同情况下的结构体使用。通过源代码分析,阐述了该函数在智能卡通信中的关键作用。

1999

被折叠的 条评论
为什么被折叠?



