winscard.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. // BSD 3-Clause License
  2. //
  3. // Copyright (c) 2019, Guillaume Ballet
  4. // All rights reserved.
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice, this
  10. // list of conditions and the following disclaimer.
  11. //
  12. // * Redistributions in binary form must reproduce the above copyright notice,
  13. // this list of conditions and the following disclaimer in the documentation
  14. // and/or other materials provided with the distribution.
  15. //
  16. // * Neither the name of the copyright holder nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23. // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. package pcsc
  31. import (
  32. "encoding/binary"
  33. "fmt"
  34. "net"
  35. "sync"
  36. "unsafe"
  37. )
  38. // Client contains all the information needed to establish
  39. // and maintain a connection to the deamon/card.
  40. type Client struct {
  41. conn net.Conn
  42. minor uint32
  43. major uint32
  44. ctx uint32
  45. mutex sync.Mutex
  46. readerStateDescriptors [MaxReaderStateDescriptors]ReaderState
  47. }
  48. // EstablishContext asks the PCSC daemon to create a context
  49. // handle for further communication with connected cards and
  50. // readers.
  51. func EstablishContext(path string, scope uint32) (*Client, error) {
  52. client := &Client{}
  53. conn, err := clientSetupSession(path)
  54. if err != nil {
  55. return nil, err
  56. }
  57. client.conn = conn
  58. payload := make([]byte, 12)
  59. response := make([]byte, 12)
  60. var code uint32
  61. var minor uint32
  62. for minor = ProtocolVersionMinor; minor <= ProtocolVersionMinor+1; minor++ {
  63. /* Exchange version information */
  64. binary.LittleEndian.PutUint32(payload, ProtocolVersionMajor)
  65. binary.LittleEndian.PutUint32(payload[4:], minor)
  66. binary.LittleEndian.PutUint32(payload[8:], SCardSuccess.Code())
  67. err = messageSendWithHeader(CommandVersion, conn, payload)
  68. if err != nil {
  69. return nil, err
  70. }
  71. n, err := conn.Read(response)
  72. if err != nil {
  73. return nil, err
  74. }
  75. if n != len(response) {
  76. return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n)
  77. }
  78. code = binary.LittleEndian.Uint32(response[8:])
  79. if code != SCardSuccess.Code() {
  80. continue
  81. }
  82. client.major = binary.LittleEndian.Uint32(response)
  83. client.minor = binary.LittleEndian.Uint32(response[4:])
  84. if client.major != ProtocolVersionMajor || client.minor != minor {
  85. continue
  86. }
  87. break
  88. }
  89. if code != SCardSuccess.Code() {
  90. return nil, fmt.Errorf("invalid response code: expected %d, got %d (%v)", SCardSuccess, code, ErrorCode(code).Error())
  91. }
  92. if client.major != ProtocolVersionMajor || (client.minor != minor && client.minor+1 != minor) {
  93. return nil, fmt.Errorf("invalid version found: expected %d.%d, got %d.%d", ProtocolVersionMajor, ProtocolVersionMinor, client.major, client.minor)
  94. }
  95. /* Establish the context proper */
  96. binary.LittleEndian.PutUint32(payload, scope)
  97. binary.LittleEndian.PutUint32(payload[4:], 0)
  98. binary.LittleEndian.PutUint32(payload[8:], SCardSuccess.Code())
  99. err = messageSendWithHeader(SCardEstablishContext, conn, payload)
  100. if err != nil {
  101. return nil, err
  102. }
  103. response = make([]byte, 12)
  104. n, err := conn.Read(response)
  105. if err != nil {
  106. return nil, err
  107. }
  108. if n != len(response) {
  109. return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n)
  110. }
  111. code = binary.LittleEndian.Uint32(response[8:])
  112. if code != SCardSuccess.Code() {
  113. return nil, fmt.Errorf("invalid response code: expected %d, got %d (%v)", SCardSuccess, code, ErrorCode(code).Error())
  114. }
  115. client.ctx = binary.LittleEndian.Uint32(response[4:])
  116. return client, nil
  117. }
  118. // ReleaseContext tells the daemon that the client will no longer
  119. // need the context.
  120. func (client *Client) ReleaseContext() error {
  121. client.mutex.Lock()
  122. defer client.mutex.Unlock()
  123. data := [8]byte{}
  124. binary.LittleEndian.PutUint32(data[:], client.ctx)
  125. binary.LittleEndian.PutUint32(data[4:], SCardSuccess.Code())
  126. err := messageSendWithHeader(SCardReleaseContext, client.conn, data[:])
  127. if err != nil {
  128. return err
  129. }
  130. total := 0
  131. for total < len(data) {
  132. n, err := client.conn.Read(data[total:])
  133. if err != nil {
  134. return err
  135. }
  136. total += n
  137. }
  138. code := binary.LittleEndian.Uint32(data[4:])
  139. if code != SCardSuccess.Code() {
  140. return fmt.Errorf("invalid return code: %x, %v", code, ErrorCode(code).Error())
  141. }
  142. return nil
  143. }
  144. // Constants related to the reader state structure
  145. const (
  146. ReaderStateNameLength = 128
  147. ReaderStateMaxAtrSizeLength = 33
  148. // NOTE: ATR is 32-byte aligned in the C version, which means it's
  149. // actually 36 byte long and not 33.
  150. ReaderStateDescriptorLength = ReaderStateNameLength + ReaderStateMaxAtrSizeLength + 5*4 + 3
  151. MaxReaderStateDescriptors = 16
  152. )
  153. // ReaderState represent the state of a single reader, as reported
  154. // by the PCSC daemon.
  155. type ReaderState struct {
  156. Name string /* reader name */
  157. eventCounter uint32 /* number of card events */
  158. readerState uint32 /* SCARD_* bit field */
  159. readerSharing uint32 /* PCSCLITE_SHARING_* sharing status */
  160. cardAtr [ReaderStateMaxAtrSizeLength]byte /* ATR */
  161. cardAtrLength uint32 /* ATR length */
  162. cardProtocol uint32 /* SCARD_PROTOCOL_* value */
  163. }
  164. func getReaderState(data []byte) (ReaderState, error) {
  165. ret := ReaderState{}
  166. if len(data) < ReaderStateDescriptorLength {
  167. return ret, fmt.Errorf("could not unmarshall data of length %d < %d", len(data), ReaderStateDescriptorLength)
  168. }
  169. ret.Name = string(data[:ReaderStateNameLength])
  170. ret.eventCounter = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.eventCounter):])
  171. ret.readerState = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerState):])
  172. ret.readerSharing = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerSharing):])
  173. copy(ret.cardAtr[:], data[unsafe.Offsetof(ret.cardAtr):unsafe.Offsetof(ret.cardAtr)+ReaderStateMaxAtrSizeLength])
  174. ret.cardAtrLength = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardAtrLength):])
  175. ret.cardProtocol = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardProtocol):])
  176. return ret, nil
  177. }
  178. // ListReaders gets the list of readers from the daemon
  179. func (client *Client) ListReaders() ([]string, error) {
  180. client.mutex.Lock()
  181. defer client.mutex.Unlock()
  182. err := messageSendWithHeader(CommandGetReaderState, client.conn, []byte{})
  183. if err != nil {
  184. return nil, err
  185. }
  186. response := make([]byte, ReaderStateDescriptorLength*MaxReaderStateDescriptors)
  187. total := 0
  188. for total < len(response) {
  189. n, err := client.conn.Read(response[total:])
  190. if err != nil {
  191. return nil, err
  192. }
  193. total += n
  194. }
  195. var names []string
  196. for i := range client.readerStateDescriptors {
  197. desc, err := getReaderState(response[i*ReaderStateDescriptorLength:])
  198. if err != nil {
  199. return nil, err
  200. }
  201. client.readerStateDescriptors[i] = desc
  202. if desc.Name[0] == 0 {
  203. break
  204. }
  205. names = append(names, desc.Name)
  206. }
  207. return names, nil
  208. }
  209. // Offsets into the Connect request/response packet
  210. const (
  211. SCardConnectReaderNameOffset = 4
  212. SCardConnectShareModeOffset = SCardConnectReaderNameOffset + ReaderStateNameLength
  213. SCardConnectPreferredProtocolOffset = SCardConnectShareModeOffset + 4
  214. SCardConnectReturnValueOffset = SCardConnectPreferredProtocolOffset + 12
  215. )
  216. // Card represents the connection to a card
  217. type Card struct {
  218. handle uint32
  219. activeProto uint32
  220. client *Client
  221. }
  222. // Connect asks the daemon to connect to the card
  223. func (client *Client) Connect(name string, shareMode uint32, preferredProtocol uint32) (*Card, error) {
  224. client.mutex.Lock()
  225. defer client.mutex.Unlock()
  226. request := make([]byte, ReaderStateNameLength+4*6)
  227. binary.LittleEndian.PutUint32(request, client.ctx)
  228. copy(request[SCardConnectReaderNameOffset:], []byte(name))
  229. binary.LittleEndian.PutUint32(request[SCardConnectShareModeOffset:], shareMode)
  230. binary.LittleEndian.PutUint32(request[SCardConnectPreferredProtocolOffset:], preferredProtocol)
  231. binary.LittleEndian.PutUint32(request[SCardConnectReturnValueOffset:], SCardSuccess.Code())
  232. err := messageSendWithHeader(SCardConnect, client.conn, request)
  233. if err != nil {
  234. return nil, err
  235. }
  236. response := make([]byte, ReaderStateNameLength+4*6)
  237. total := 0
  238. for total < len(response) {
  239. n, err := client.conn.Read(response[total:])
  240. if err != nil {
  241. return nil, err
  242. }
  243. // fmt.Println("total, n", total, n, response)
  244. total += n
  245. }
  246. code := binary.LittleEndian.Uint32(response[148:])
  247. if code != SCardSuccess.Code() {
  248. return nil, fmt.Errorf("invalid return code: %x (%v)", code, ErrorCode(code).Error())
  249. }
  250. handle := binary.LittleEndian.Uint32(response[140:])
  251. active := binary.LittleEndian.Uint32(response[SCardConnectPreferredProtocolOffset:])
  252. return &Card{handle: handle, activeProto: active, client: client}, nil
  253. }
  254. /**
  255. * @brief contained in \ref SCARD_TRANSMIT Messages.
  256. *
  257. * These data are passed throw the field \c sharedSegmentMsg.data.
  258. */
  259. type transmit struct {
  260. hCard uint32
  261. ioSendPciProtocol uint32
  262. ioSendPciLength uint32
  263. cbSendLength uint32
  264. ioRecvPciProtocol uint32
  265. ioRecvPciLength uint32
  266. pcbRecvLength uint32
  267. rv uint32
  268. }
  269. // SCardIoRequest contains the info needed for performing an IO request
  270. type SCardIoRequest struct {
  271. proto uint32
  272. length uint32
  273. }
  274. const (
  275. TransmitRequestLength = 32
  276. )
  277. // Transmit sends request data to a card and returns the response
  278. func (card *Card) Transmit(adpu []byte) ([]byte, *SCardIoRequest, error) {
  279. card.client.mutex.Lock()
  280. defer card.client.mutex.Unlock()
  281. request := [TransmitRequestLength]byte{}
  282. binary.LittleEndian.PutUint32(request[:], card.handle)
  283. binary.LittleEndian.PutUint32(request[4:] /*card.activeProto*/, 2)
  284. binary.LittleEndian.PutUint32(request[8:], 8)
  285. binary.LittleEndian.PutUint32(request[12:], uint32(len(adpu)))
  286. binary.LittleEndian.PutUint32(request[16:], 0)
  287. binary.LittleEndian.PutUint32(request[20:], 0)
  288. binary.LittleEndian.PutUint32(request[24:], 0x10000)
  289. binary.LittleEndian.PutUint32(request[28:], SCardSuccess.Code())
  290. err := messageSendWithHeader(SCardTransmit, card.client.conn, request[:])
  291. if err != nil {
  292. return nil, nil, err
  293. }
  294. // Add the ADPU payload after the transmit descriptor
  295. n, err := card.client.conn.Write(adpu)
  296. if err != nil {
  297. return nil, nil, err
  298. }
  299. if n != len(adpu) {
  300. return nil, nil, fmt.Errorf("Invalid number of bytes written: expected %d, got %d", len(adpu), n)
  301. }
  302. response := [TransmitRequestLength]byte{}
  303. total := 0
  304. for total < len(response) {
  305. n, err = card.client.conn.Read(response[total:])
  306. if err != nil {
  307. return nil, nil, err
  308. }
  309. total += n
  310. }
  311. code := binary.LittleEndian.Uint32(response[28:])
  312. if code != SCardSuccess.Code() {
  313. return nil, nil, fmt.Errorf("invalid return code: %x (%v)", code, ErrorCode(code).Error())
  314. }
  315. // Recover the response data
  316. recvProto := binary.LittleEndian.Uint32(response[16:])
  317. recvLength := binary.LittleEndian.Uint32(response[20:])
  318. recv := &SCardIoRequest{proto: recvProto, length: recvLength}
  319. recvLength = binary.LittleEndian.Uint32(response[24:])
  320. recvData := make([]byte, recvLength)
  321. total = 0
  322. for uint32(total) < recvLength {
  323. n, err := card.client.conn.Read(recvData[total:])
  324. if err != nil {
  325. return nil, nil, err
  326. }
  327. total += n
  328. }
  329. return recvData, recv, nil
  330. }
  331. // Disconnect tells the PCSC daemon that the client is no longer
  332. // interested in communicating with the card.
  333. func (card *Card) Disconnect(disposition uint32) error {
  334. card.client.mutex.Lock()
  335. defer card.client.mutex.Unlock()
  336. data := [12]byte{}
  337. binary.LittleEndian.PutUint32(data[:], card.handle)
  338. binary.LittleEndian.PutUint32(data[4:], disposition)
  339. binary.LittleEndian.PutUint32(data[8:], SCardSuccess.Code())
  340. err := messageSendWithHeader(SCardDisConnect, card.client.conn, data[:])
  341. if err != nil {
  342. return err
  343. }
  344. total := 0
  345. for total < len(data) {
  346. n, err := card.client.conn.Read(data[total:])
  347. if err != nil {
  348. return err
  349. }
  350. total += n
  351. }
  352. code := binary.LittleEndian.Uint32(data[8:])
  353. if code != SCardSuccess.Code() {
  354. return fmt.Errorf("invalid return code: %x (%v)", code, ErrorCode(code).Error())
  355. }
  356. return nil
  357. }