Remove the direct dependency on libpcsclite

Instead, use a go library that communicates with pcscd over a socket.

Also update the changes introduced by @gravityblast since this PR's
inception
This commit is contained in:
Guillaume Ballet
2019-03-14 23:03:13 +01:00
parent ae82c58631
commit 5617dca1c9
18 changed files with 678 additions and 1192 deletions

29
vendor/github.com/gballet/go-libpcsclite/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, Guillaume Ballet
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

53
vendor/github.com/gballet/go-libpcsclite/README.md generated vendored Normal file
View File

@@ -0,0 +1,53 @@
# go-libpcsclite
A golang implementation of the [libpcpsclite](http://github.com/LudovicRousseau/PCSC) client. It connects to the `pcscd` daemon over sockets.
## Purpose
The goal is for major open source projects to distribute a single binary that doesn't depend on `libpcsclite`. It provides an extra function `CheckPCSCDaemon` that will tell the user if `pcscd` is running.
## Building
TODO
## Example
TODO
## TODO
- [ ] Finish this README
- [ ] Lock context
- [ ] implement missing functions
## License
BSD 3-Clause License
Copyright (c) 2019, Guillaume Ballet
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

99
vendor/github.com/gballet/go-libpcsclite/doc.go generated vendored Normal file
View File

@@ -0,0 +1,99 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package pcsc
const (
SCardSuccess = 0x00000000 /* No error was encountered. */
AutoAllocate = -1 /* see SCardFreeMemory() */
ScopeUser = 0x0000 /* Scope in user space */
ScopeTerminal = 0x0001 /* Scope in terminal */
ScopeSystem = 0x0002 /* Scope in system */
ScopeGlobal = 0x0003 /* Scope is global */
ProtocolUndefined = 0x0000 /* protocol not set */
ProtocolUnSet = ProtocolUndefined /* backward compat */
ProtocolT0 = 0x0001 /* T=0 active protocol. */
ProtocolT1 = 0x0002 /* T=1 active protocol. */
ProtocolRaw = 0x0004 /* Raw active protocol. */
ProtocolT15 = 0x0008 /* T=15 protocol. */
ProtocolAny = (ProtocolT0 | ProtocolT1) /* IFD determines prot. */
ShareExclusive = 0x0001 /* Exclusive mode only */
ShareShared = 0x0002 /* Shared mode only */
ShareDirect = 0x0003 /* Raw mode only */
LeaveCard = 0x0000 /* Do nothing on close */
ResetCard = 0x0001 /* Reset on close */
UnpowerCard = 0x0002 /* Power down on close */
EjectCard = 0x0003 /* Eject on close */
SCardUnknown = 0x0001 /* Unknown state */
SCardAbsent = 0x0002 /* Card is absent */
SCardPresent = 0x0004 /* Card is present */
SCardSwallowed = 0x0008 /* Card not powered */
SCardPowever = 0x0010 /* Card is powered */
SCardNegotiable = 0x0020 /* Ready for PTS */
SCardSpecific = 0x0040 /* PTS has been set */
PCSCDSockName = "/run/pcscd/pcscd.comm"
)
// List of commands to send to the daemon
const (
_ = iota
SCardEstablishContext /* used by SCardEstablishContext() */
SCardReleaseContext /* used by SCardReleaseContext() */
SCardListReaders /* used by SCardListReaders() */
SCardConnect /* used by SCardConnect() */
SCardReConnect /* used by SCardReconnect() */
SCardDisConnect /* used by SCardDisconnect() */
SCardBeginTransaction /* used by SCardBeginTransaction() */
SCardEndTransaction /* used by SCardEndTransaction() */
SCardTransmit /* used by SCardTransmit() */
SCardControl /* used by SCardControl() */
SCardStatus /* used by SCardStatus() */
SCardGetStatusChange /* not used */
SCardCancel /* used by SCardCancel() */
SCardCancelTransaction /* not used */
SCardGetAttrib /* used by SCardGetAttrib() */
SCardSetAttrib /* used by SCardSetAttrib() */
CommandVersion /* get the client/server protocol version */
CommandGetReaderState /* get the readers state */
CommandWaitReaderStateChange /* wait for a reader state change */
CommandStopWaitingReaderStateChange /* stop waiting for a reader state change */
)
// Protocol information
const (
ProtocolVersionMajor = 4 /* IPC major */
ProtocolVersionMinor = 3 /* IPC minor */
)

1
vendor/github.com/gballet/go-libpcsclite/go.mod generated vendored Normal file
View File

@@ -0,0 +1 @@
module github.com/gballet/go-libpcsclite

78
vendor/github.com/gballet/go-libpcsclite/msg.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package pcsc
import (
"encoding/binary"
"net"
)
/**
* @brief Wrapper for the MessageSend() function.
*
* Called by clients to send messages to the server.
* The parameters \p command and \p data are set in the \c sharedSegmentMsg
* struct in order to be sent.
*
* @param[in] command Command to be sent.
* @param[in] dwClientID Client socket handle.
* @param[in] size Size of the message (\p data).
* @param[in] data_void Data to be sent.
*
* @return Same error codes as MessageSend().
*/
func messageSendWithHeader(command uint32, conn net.Conn, data []byte) error {
/* Translate header into bytes */
msgData := make([]byte, 8+len(data))
binary.LittleEndian.PutUint32(msgData[4:], command)
binary.LittleEndian.PutUint32(msgData, uint32(len(data)))
/* Copy payload */
copy(msgData[8:], data)
_, err := conn.Write(msgData)
return err
}
// ClientSetupSession prepares a communication channel for the client to talk to the server.
// This is called by the application to create a socket for local IPC with the
// server. The socket is associated to the file \c PCSCLITE_CSOCK_NAME.
/*
* @param[out] pdwClientID Client Connection ID.
*
* @retval 0 Success.
* @retval -1 Can not create the socket.
* @retval -1 The socket can not open a connection.
* @retval -1 Can not set the socket to non-blocking.
*/
func clientSetupSession() (net.Conn, error) {
return net.Dial("unix", PCSCDSockName)
}

371
vendor/github.com/gballet/go-libpcsclite/winscard.go generated vendored Normal file
View File

@@ -0,0 +1,371 @@
// BSD 3-Clause License
//
// Copyright (c) 2019, Guillaume Ballet
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package pcsc
import (
"encoding/binary"
"fmt"
"net"
"unsafe"
)
// Client contains all the information needed to establish
// and maintain a connection to the deamon/card.
type Client struct {
conn net.Conn
minor uint32
major uint32
ctx uint32
readerStateDescriptors [MaxReaderStateDescriptors]ReaderState
}
// EstablishContext asks the PCSC daemon to create a context
// handle for further communication with connected cards and
// readers.
func EstablishContext(scope uint32) (*Client, error) {
client := &Client{}
conn, err := clientSetupSession()
if err != nil {
return nil, err
}
client.conn = conn
/* Exchange version information */
payload := make([]byte, 12)
binary.LittleEndian.PutUint32(payload, ProtocolVersionMajor)
binary.LittleEndian.PutUint32(payload[4:], ProtocolVersionMinor)
binary.LittleEndian.PutUint32(payload[8:], SCardSuccess)
err = messageSendWithHeader(CommandVersion, conn, payload)
if err != nil {
return nil, err
}
response := make([]byte, 12)
n, err := conn.Read(response)
if err != nil {
return nil, err
}
if n != len(response) {
return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n)
}
code := binary.LittleEndian.Uint32(response[8:])
if code != SCardSuccess {
return nil, fmt.Errorf("invalid response code: expected %d, got %d", SCardSuccess, code)
}
client.major = binary.LittleEndian.Uint32(response)
client.minor = binary.LittleEndian.Uint32(response[4:])
if client.major != ProtocolVersionMajor || client.minor != ProtocolVersionMinor {
return nil, fmt.Errorf("invalid version found: expected %d.%d, got %d.%d", ProtocolVersionMajor, ProtocolVersionMinor, client.major, client.minor)
}
/* Establish the context proper */
binary.LittleEndian.PutUint32(payload, scope)
binary.LittleEndian.PutUint32(payload[4:], 0)
binary.LittleEndian.PutUint32(payload[8:], SCardSuccess)
err = messageSendWithHeader(SCardEstablishContext, conn, payload)
if err != nil {
return nil, err
}
response = make([]byte, 12)
n, err = conn.Read(response)
if err != nil {
return nil, err
}
if n != len(response) {
return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n)
}
code = binary.LittleEndian.Uint32(response[8:])
if code != SCardSuccess {
return nil, fmt.Errorf("invalid response code: expected %d, got %d", SCardSuccess, code)
}
client.ctx = binary.LittleEndian.Uint32(response[4:])
return client, nil
}
// ReleaseContext tells the daemon that the client will no longer
// need the context.
func (client *Client) ReleaseContext() error {
data := [8]byte{}
binary.LittleEndian.PutUint32(data[:], client.ctx)
binary.LittleEndian.PutUint32(data[4:], SCardSuccess)
err := messageSendWithHeader(SCardReleaseContext, client.conn, data[:])
if err != nil {
return err
}
total := 0
for total < len(data) {
n, err := client.conn.Read(data[total:])
if err != nil {
return err
}
total += n
}
code := binary.LittleEndian.Uint32(data[4:])
if code != SCardSuccess {
return fmt.Errorf("invalid return code: %x", code)
}
return nil
}
// Constants related to the reader state structure
const (
ReaderStateNameLength = 128
ReaderStateMaxAtrSizeLength = 33
// NOTE: ATR is 32-byte aligned in the C version, which means it's
// actually 36 byte long and not 33.
ReaderStateDescriptorLength = ReaderStateNameLength + ReaderStateMaxAtrSizeLength + 5*4 + 3
MaxReaderStateDescriptors = 16
)
// ReaderState represent the state of a single reader, as reported
// by the PCSC daemon.
type ReaderState struct {
Name string /* reader name */
eventCounter uint32 /* number of card events */
readerState uint32 /* SCARD_* bit field */
readerSharing uint32 /* PCSCLITE_SHARING_* sharing status */
cardAtr [ReaderStateMaxAtrSizeLength]byte /* ATR */
cardAtrLength uint32 /* ATR length */
cardProtocol uint32 /* SCARD_PROTOCOL_* value */
}
func getReaderState(data []byte) (ReaderState, error) {
ret := ReaderState{}
if len(data) < ReaderStateDescriptorLength {
return ret, fmt.Errorf("could not unmarshall data of length %d < %d", len(data), ReaderStateDescriptorLength)
}
ret.Name = string(data[:ReaderStateNameLength])
ret.eventCounter = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.eventCounter):])
ret.readerState = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerState):])
ret.readerSharing = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerSharing):])
copy(ret.cardAtr[:], data[unsafe.Offsetof(ret.cardAtr):unsafe.Offsetof(ret.cardAtr)+ReaderStateMaxAtrSizeLength])
ret.cardAtrLength = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardAtrLength):])
ret.cardProtocol = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardProtocol):])
return ret, nil
}
// ListReaders gets the list of readers from the daemon
func (client *Client) ListReaders() ([]string, error) {
err := messageSendWithHeader(CommandGetReaderState, client.conn, []byte{})
if err != nil {
return nil, err
}
response := make([]byte, ReaderStateDescriptorLength*MaxReaderStateDescriptors)
total := 0
for total < len(response) {
n, err := client.conn.Read(response[total:])
if err != nil {
return nil, err
}
total += n
}
var names []string
for i := range client.readerStateDescriptors {
desc, err := getReaderState(response[i*ReaderStateDescriptorLength:])
if err != nil {
return nil, err
}
client.readerStateDescriptors[i] = desc
if desc.Name[0] == 0 {
break
}
names = append(names, desc.Name)
}
return names, nil
}
// Offsets into the Connect request/response packet
const (
SCardConnectReaderNameOffset = 4
SCardConnectShareModeOffset = SCardConnectReaderNameOffset + ReaderStateNameLength
SCardConnectPreferredProtocolOffset = SCardConnectShareModeOffset + 4
SCardConnectReturnValueOffset = SCardConnectPreferredProtocolOffset + 12
)
// Card represents the connection to a card
type Card struct {
handle uint32
activeProto uint32
client *Client
}
// Connect asks the daemon to connect to the card
func (client *Client) Connect(name string, shareMode uint32, preferredProtocol uint32) (*Card, error) {
request := make([]byte, ReaderStateNameLength+4*6)
binary.LittleEndian.PutUint32(request, client.ctx)
copy(request[SCardConnectReaderNameOffset:], []byte(name))
binary.LittleEndian.PutUint32(request[SCardConnectShareModeOffset:], shareMode)
binary.LittleEndian.PutUint32(request[SCardConnectPreferredProtocolOffset:], preferredProtocol)
binary.LittleEndian.PutUint32(request[SCardConnectReturnValueOffset:], SCardSuccess)
err := messageSendWithHeader(SCardConnect, client.conn, request)
if err != nil {
return nil, err
}
response := make([]byte, ReaderStateNameLength+4*6)
total := 0
for total < len(response) {
n, err := client.conn.Read(response[total:])
if err != nil {
return nil, err
}
fmt.Println("total, n", total, n, response)
total += n
}
code := binary.LittleEndian.Uint32(response[148:])
if code != SCardSuccess {
return nil, fmt.Errorf("invalid return code: %x", code)
}
handle := binary.LittleEndian.Uint32(response[140:])
active := binary.LittleEndian.Uint32(response[SCardConnectPreferredProtocolOffset:])
return &Card{handle: handle, activeProto: active, client: client}, nil
}
/**
* @brief contained in \ref SCARD_TRANSMIT Messages.
*
* These data are passed throw the field \c sharedSegmentMsg.data.
*/
type transmit struct {
hCard uint32
ioSendPciProtocol uint32
ioSendPciLength uint32
cbSendLength uint32
ioRecvPciProtocol uint32
ioRecvPciLength uint32
pcbRecvLength uint32
rv uint32
}
// SCardIoRequest contains the info needed for performing an IO request
type SCardIoRequest struct {
proto uint32
length uint32
}
const (
TransmitRequestLength = 32
)
// Transmit sends request data to a card and returns the response
func (card *Card) Transmit(adpu []byte) ([]byte, *SCardIoRequest, error) {
request := [TransmitRequestLength]byte{}
binary.LittleEndian.PutUint32(request[:], card.handle)
binary.LittleEndian.PutUint32(request[4:] /*card.activeProto*/, 2)
binary.LittleEndian.PutUint32(request[8:], 8)
binary.LittleEndian.PutUint32(request[12:], uint32(len(adpu)))
binary.LittleEndian.PutUint32(request[16:], 0)
binary.LittleEndian.PutUint32(request[20:], 0)
binary.LittleEndian.PutUint32(request[24:], 0x10000)
binary.LittleEndian.PutUint32(request[28:], SCardSuccess)
err := messageSendWithHeader(SCardTransmit, card.client.conn, request[:])
if err != nil {
return nil, nil, err
}
// Add the ADPU payload after the transmit descriptor
n, err := card.client.conn.Write(adpu)
if err != nil {
return nil, nil, err
}
if n != len(adpu) {
return nil, nil, fmt.Errorf("Invalid number of bytes written: expected %d, got %d", len(adpu), n)
}
response := [TransmitRequestLength]byte{}
total := 0
for total < len(response) {
n, err = card.client.conn.Read(response[total:])
if err != nil {
return nil, nil, err
}
total += n
}
code := binary.LittleEndian.Uint32(response[28:])
if code != SCardSuccess {
return nil, nil, fmt.Errorf("invalid return code: %x", code)
}
// Recover the response data
recvProto := binary.LittleEndian.Uint32(response[16:])
recvLength := binary.LittleEndian.Uint32(response[20:])
recv := &SCardIoRequest{proto: recvProto, length: recvLength}
recvLength = binary.LittleEndian.Uint32(response[24:])
recvData := make([]byte, recvLength)
total = 0
for uint32(total) < recvLength {
n, err := card.client.conn.Read(recvData[total:])
if err != nil {
return nil, nil, err
}
total += n
}
return recvData, recv, nil
}
// Disconnect tells the PCSC daemon that the client is no longer
// interested in communicating with the card.
func (card *Card) Disconnect(disposition uint32) error {
data := [12]byte{}
binary.LittleEndian.PutUint32(data[:], card.handle)
binary.LittleEndian.PutUint32(data[4:], disposition)
binary.LittleEndian.PutUint32(data[8:], SCardSuccess)
err := messageSendWithHeader(SCardDisConnect, card.client.conn, data[:])
if err != nil {
return err
}
total := 0
for total < len(data) {
n, err := card.client.conn.Read(data[total:])
if err != nil {
return err
}
total += n
}
code := binary.LittleEndian.Uint32(data[8:])
if code != SCardSuccess {
return fmt.Errorf("invalid return code: %x", code)
}
return nil
}