-
Notifications
You must be signed in to change notification settings - Fork 1
/
scard_linux.go
268 lines (234 loc) · 8.62 KB
/
scard_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
//go:build linux
// +build linux
// Copyright (c) 2023, El Mostafa IDRASSI.
//
// 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.
package goscard
import (
"fmt"
"unsafe"
)
type hnd int
type dword uint
type scardRet int
type str *byte
//////////////////////////////////////////////////////////////////////////////////////
// pcsclite.h.in
//////////////////////////////////////////////////////////////////////////////////////
const maxBufferSizeExtended dword = (4 + 3 + (1 << 16) + 3 + 2) // Enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
//////////////////////////////////////////////////////////////////////////////////////
// reader.h
//////////////////////////////////////////////////////////////////////////////////////
const (
FeatureExecutePACE Feature = 0x20
)
type PcscV2Part10Property dword
// Properties returned by FEATURE_GET_TLV_PROPERTIES
const (
PcscV2Part10Property_wLcdLayout PcscV2Part10Property = 1
PcscV2Part10Property_bEntryValidationCondition PcscV2Part10Property = 2
PcscV2Part10Property_bTimeOut2 PcscV2Part10Property = 3
PcscV2Part10Property_wLcdMaxCharacters PcscV2Part10Property = 4
PcscV2Part10Property_wLcdMaxLines PcscV2Part10Property = 5
PcscV2Part10Property_bMinPINSize PcscV2Part10Property = 6
PcscV2Part10Property_bMaxPINSize PcscV2Part10Property = 7
PcscV2Part10Property_sFirmwareID PcscV2Part10Property = 8
PcscV2Part10Property_bPPDUSupport PcscV2Part10Property = 9
PcscV2Part10Property_dwMaxAPDUDataSize PcscV2Part10Property = 10
PcscV2Part10Property_wIdVendor PcscV2Part10Property = 11
PcscV2Part10Property_wIdProduct PcscV2Part10Property = 12
)
var cmIoctlGetFeatureRequest SCardCtlCode = scardCtlCodeFunc(3400)
//////////////////////////////////////////////////////////////////////////////////////
// ccid_ifdhandler.h
//////////////////////////////////////////////////////////////////////////////////////
type DriverOption dword
const (
DriverOptionCcidExchangeAuthorized DriverOption = 1
DriverOptionGempcTwinKeyApdu DriverOption = 2
DriverOptionUseBogusFirmware DriverOption = 4
DriverOptionDisablePinRetries DriverOption = (1 << 6)
)
//////////////////////////////////////////////////////////////////////////////////////
// winscard.h
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// DLL references.
//////////////////////////////////////////////////////////////////////////////////////
type scardGetStatusChange func(
hContext SCardContext, // in
dwTimeout dword, // in
rgReaderStates *scardReaderState, // in, out
cReaders dword, // in
) dword
type scardControl func(
hCard SCardHandle, // in
dwControlCode SCardCtlCode, // in
pbSendBuffer *byte, // in
cbSendLength dword, // in
pbRecvBuffer *byte, // out
cbRecvLength dword, // in
lpBytesReturned *dword, // out
) dword
// GetStatusChange is a wrapper around SCardGetStatusChange.
//
// This function blocks execution until the current availability of
// the cards in a specific set of readers changes.
// This function receives a structure or list of structures
// containing reader names. It then blocks waiting for a
// change in state to occur for a maximum blocking time of
// timeout or forever if InfiniteTimeout is used.
// The new event state will be contained in EventState.
// A status change might be a card insertion or removal event,
// a change in ATR, etc.
// EventState also contains a number of events in the upper
// 16 bits (EventState & 0xFFFF0000). This number of events
// is incremented for each card insertion or removal in the
// specified reader. This can be used to detect a card
// removal/insertion between two calls to SCardGetStatusChange()
// To wait for a reader event (reader added or removed) you may
// use the special reader name "\\?PnP?\Notification".
// If a reader event occurs the state of this reader will change
// and the bit SCardStateChanged will be set.
// To cancel the ongoing call, use SCardCancel() with the same
// SCardContext.
//
// N.B: The function will update the SCardStates inside of the
// passed SCardReaderState array.
func (c *Context) GetStatusChange(
timeout Timeout,
readerStates []SCardReaderState,
) (ret uint64, err error) {
defer func() {
if err != nil {
logger.Error(err)
}
}()
var internalReaderStates []scardReaderState
var internalReaderStatesPtr *scardReaderState
var readersNamesLen []int // need to keep the length of the reader name to be able to convert the *byte back to a string
logger.Infof("GetStatusChange, IN : (context=0x%X, timeout=%vms, readerStates=%v)",
c.ctx, timeout.Milliseconds(), readerStates)
defer func() { logger.Infof("GetStatusChange, OUT: (context=0x%X, readerStates=%v)", c.ctx, readerStates) }()
if scardGetStatusChangeProc == nil {
err = fmt.Errorf("scardGetStatusChange() not found in pcsclite")
return
}
// We need to convert back and forth between
// SCardReaderState and scardReaderStateInternal.
if len(readerStates) > 0 {
internalReaderStates = make([]scardReaderState, len(readerStates))
readersNamesLen = make([]int, len(readerStates))
for i, readerState := range readerStates {
internalReaderStates[i], readersNamesLen[i], err = readerState.toInternal()
if err != nil {
return
}
}
internalReaderStatesPtr = (*scardReaderState)(unsafe.Pointer(&internalReaderStates[0]))
}
r := scardGetStatusChangeProc(
c.ctx, /* SCARDCONTEXT */
dword(timeout.Milliseconds()), /* DWORD */
internalReaderStatesPtr, /* LPSCARD_READERSTATEW */
dword(len(readerStates)), /* DWORD */
)
if r != 0 {
var msg error
if pcscErr := maybePcscErr(r); pcscErr != nil {
msg = pcscErr
}
ret = uint64(r)
err = fmt.Errorf("scardGetStatusChange() returned 0x%X [%v]", r, msg)
return
}
for i, internalReaderState := range internalReaderStates {
readerStates[i].fromInternal(internalReaderState, readersNamesLen[i])
}
return
}
// Control is a wrapper around SCardControl.
//
// This function sends a command directly to the IFD Handler
// (reader driver) to be processed by the reader.
// This is useful for creating client side reader
// drivers for functions like PIN pads, biometrics,
// or other extensions to the normal smart card
// reader that are not normally handled by PC/SC.
//
// N.B: This function implements handling of the case
// of SCARD_E_INSUFFICIENT_BUFFER (0x80100008) internally.
func (c *Card) Control(
scardControlCode SCardCtlCode,
inBuffer []byte,
) (outBuffer []byte, ret uint64, err error) {
defer func() {
if err != nil {
logger.Error(err)
}
}()
var inBufferPtr *byte
var outBufferPtr *byte
var bytesReturned dword
logger.Infof("Control, IN : (handle=0x%X, inBuffer=%v)", c.handle, inBuffer)
defer func() { logger.Infof("Control, OUT: (handle=0x%X, outBuffer=%v)", c.handle, outBuffer) }()
if scardControlProc == nil {
err = fmt.Errorf("scardControl() not found in pcsclite")
return
}
if len(inBuffer) > 0 {
inBufferPtr = &inBuffer[0]
}
// We use the short APDU buffer size.
// If this is not sufficient, the card will let us know
// and we'll use the returned size.
outBufferSize := dword(maxBufferSize)
outBuffer = make([]byte, outBufferSize)
outBufferPtr = &outBuffer[0]
r := scardControlProc(
c.handle, /* SCARDHANDLE */
scardControlCode, /* DWORD */
inBufferPtr,
dword(len(inBuffer)),
outBufferPtr,
outBufferSize,
&bytesReturned,
)
if r == 0x80100008 && bytesReturned > 0 { // SCARD_E_INSUFFICIENT_BUFFER
outBuffer = make([]byte, bytesReturned)
outBufferPtr = &outBuffer[0]
r = scardControlProc(
c.handle, /* SCARDHANDLE */
scardControlCode, /* DWORD */
inBufferPtr,
dword(len(inBuffer)),
outBufferPtr,
outBufferSize,
&bytesReturned,
)
}
if r != 0 {
var msg error
if pcscErr := maybePcscErr(r); pcscErr != nil {
msg = pcscErr
}
outBuffer = nil
ret = uint64(r)
err = fmt.Errorf("scardControl() returned 0x%X [%v]", r, msg)
return
}
if bytesReturned > 0 && outBuffer != nil {
outBuffer = outBuffer[:bytesReturned]
}
return
}