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
pull/19273/head
Guillaume Ballet 6 years ago
parent ae82c58631
commit 5617dca1c9
  1. 10
      accounts/scwallet/hub.go
  2. 32
      accounts/scwallet/securechannel.go
  3. 31
      accounts/scwallet/wallet.go
  4. 4
      console/bridge.go
  5. 23
      vendor/github.com/ebfe/scard/LICENSE
  6. 14
      vendor/github.com/ebfe/scard/README.md
  7. 283
      vendor/github.com/ebfe/scard/scard.go
  8. 219
      vendor/github.com/ebfe/scard/scard_darwin.go
  9. 206
      vendor/github.com/ebfe/scard/scard_unix.go
  10. 221
      vendor/github.com/ebfe/scard/scard_windows.go
  11. 190
      vendor/github.com/ebfe/scard/zconst.go
  12. 29
      vendor/github.com/gballet/go-libpcsclite/LICENSE
  13. 53
      vendor/github.com/gballet/go-libpcsclite/README.md
  14. 99
      vendor/github.com/gballet/go-libpcsclite/doc.go
  15. 1
      vendor/github.com/gballet/go-libpcsclite/go.mod
  16. 78
      vendor/github.com/gballet/go-libpcsclite/msg.go
  17. 371
      vendor/github.com/gballet/go-libpcsclite/winscard.go
  18. 6
      vendor/vendor.json

@ -40,11 +40,11 @@ import (
"sync"
"time"
"github.com/ebfe/scard"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
pcsc "github.com/gballet/go-libpcsclite"
)
// Scheme is the URI prefix for smartcard wallets.
@ -70,7 +70,7 @@ type smartcardPairing struct {
type Hub struct {
scheme string // Protocol scheme prefixing account and wallet URLs.
context *scard.Context
context *pcsc.Client
datadir string
pairings map[string]smartcardPairing
@ -152,7 +152,7 @@ func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
// NewHub creates a new hardware wallet manager for smartcards.
func NewHub(scheme string, datadir string) (*Hub, error) {
context, err := scard.EstablishContext()
context, err := pcsc.EstablishContext(pcsc.ScopeSystem)
if err != nil {
return nil, err
}
@ -228,7 +228,7 @@ func (hub *Hub) refreshWallets() {
delete(hub.wallets, reader)
}
// New card detected, try to connect to it
card, err := hub.context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny)
if err != nil {
log.Debug("Failed to open smart card", "reader", reader, "err", err)
continue
@ -236,7 +236,7 @@ func (hub *Hub) refreshWallets() {
wallet := NewWallet(hub, card)
if err = wallet.connect(); err != nil {
log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
card.Disconnect(scard.LeaveCard)
card.Disconnect(pcsc.LeaveCard)
continue
}
// Card connected, start tracking in amongs the wallets

@ -25,9 +25,11 @@ import (
"crypto/sha512"
"fmt"
"github.com/ebfe/scard"
"github.com/ethereum/go-ethereum/crypto"
pcsc "github.com/gballet/go-libpcsclite"
"github.com/wsddn/go-ecdh"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/text/unicode/norm"
)
const (
@ -42,22 +44,24 @@ const (
insMutuallyAuthenticate = 0x11
insPair = 0x12
insUnpair = 0x13
pairingSalt = "Keycard Pairing Password Salt"
)
// SecureChannelSession enables secure communication with a hardware wallet.
type SecureChannelSession struct {
card *scard.Card // A handle to the smartcard for communication
secret []byte // A shared secret generated from our ECDSA keys
publicKey []byte // Our own ephemeral public key
PairingKey []byte // A permanent shared secret for a pairing, if present
sessionEncKey []byte // The current session encryption key
sessionMacKey []byte // The current session MAC key
iv []byte // The current IV
PairingIndex uint8 // The pairing index
card *pcsc.Card // A handle to the smartcard for communication
secret []byte // A shared secret generated from our ECDSA keys
publicKey []byte // Our own ephemeral public key
PairingKey []byte // A permanent shared secret for a pairing, if present
sessionEncKey []byte // The current session encryption key
sessionMacKey []byte // The current session MAC key
iv []byte // The current IV
PairingIndex uint8 // The pairing index
}
// NewSecureChannelSession creates a new secure channel for the given card and public key.
func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSession, error) {
func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) {
// Generate an ECDSA keypair for ourselves
gen := ecdh.NewEllipticECDH(crypto.S256())
private, public, err := gen.GenerateKey(rand.Reader)
@ -83,8 +87,8 @@ func NewSecureChannelSession(card *scard.Card, keyData []byte) (*SecureChannelSe
}
// Pair establishes a new pairing with the smartcard.
func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
secretHash := sha256.Sum256(sharedSecret)
func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
secretHash := pbkdf2.Key(norm.NFKD.Bytes([]byte(pairingPassword)), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New)
challenge := make([]byte, 32)
if _, err := rand.Read(challenge); err != nil {
@ -102,10 +106,10 @@ func (s *SecureChannelSession) Pair(sharedSecret []byte) error {
expectedCryptogram := md.Sum(nil)
cardCryptogram := response.Data[:32]
cardChallenge := response.Data[32:]
cardChallenge := response.Data[32:64]
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
return fmt.Errorf("Invalid card cryptogram")
return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
}
md.Reset()

@ -32,7 +32,6 @@ import (
"sync"
"time"
"github.com/ebfe/scard"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
@ -40,12 +39,13 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/log"
pcsc "github.com/gballet/go-libpcsclite"
)
// ErrPUKNeeded is returned if opening the smart card requires pairing with a PUK
// code. In this case, the calling application should request user input to enter
// the PUK and send it back.
var ErrPUKNeeded = errors.New("smartcard: puk needed")
// ErrPairingPasswordNeeded is returned if opening the smart card requires pairing with a pairing
// password. In this case, the calling application should request user input to enter
// the pairing password and send it back.
var ErrPairingPasswordNeeded = errors.New("smartcard: pairing password needed")
// ErrPINNeeded is returned if opening the smart card requires a PIN code. In
// this case, the calling application should request user input to enter the PIN
@ -67,7 +67,8 @@ var ErrAlreadyOpen = errors.New("smartcard: already open")
var ErrPubkeyMismatch = errors.New("smartcard: recovered public key mismatch")
var (
appletAID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70}
// appletAID = []byte{0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x57, 0x61, 0x6C, 0x6C, 0x65, 0x74, 0x41, 0x70, 0x70}
appletAID = []byte{0xA0, 0x00, 0x00, 0x08, 0x04, 0x00, 0x01, 0x01, 0x01}
DerivationSignatureHash = sha256.Sum256([]byte("STATUS KEY DERIVATION"))
)
@ -108,10 +109,10 @@ type Wallet struct {
Hub *Hub // A handle to the Hub that instantiated this wallet.
PublicKey []byte // The wallet's public key (used for communication and identification, not signing!)
lock sync.Mutex // Lock that gates access to struct fields and communication with the card
card *scard.Card // A handle to the smartcard interface for the wallet.
session *Session // The secure communication session with the card
log log.Logger // Contextual logger to tag the base with its id
lock sync.Mutex // Lock that gates access to struct fields and communication with the card
card *pcsc.Card // A handle to the smartcard interface for the wallet.
session *Session // The secure communication session with the card
log log.Logger // Contextual logger to tag the base with its id
deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery
deriveNextAddr common.Address // Next derived account address for auto-discovery
@ -121,7 +122,7 @@ type Wallet struct {
}
// NewWallet constructs and returns a new Wallet instance.
func NewWallet(hub *Hub, card *scard.Card) *Wallet {
func NewWallet(hub *Hub, card *pcsc.Card) *Wallet {
wallet := &Wallet{
Hub: hub,
card: card,
@ -132,13 +133,13 @@ func NewWallet(hub *Hub, card *scard.Card) *Wallet {
// transmit sends an APDU to the smartcard and receives and decodes the response.
// It automatically handles requests by the card to fetch the return data separately,
// and returns an error if the response status code is not success.
func transmit(card *scard.Card, command *commandAPDU) (*responseAPDU, error) {
func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
data, err := command.serialize()
if err != nil {
return nil, err
}
responseData, err := card.Transmit(data)
responseData, _, err := card.Transmit(data)
if err != nil {
return nil, err
}
@ -349,7 +350,7 @@ func (w *Wallet) Open(passphrase string) error {
} else {
// If no passphrase was supplied, request the PUK from the user
if passphrase == "" {
return ErrPUKNeeded
return ErrPairingPasswordNeeded
}
// Attempt to pair the smart card with the user supplied PUK
if err := w.pair([]byte(passphrase)); err != nil {
@ -814,7 +815,7 @@ func (s *Session) unblockPin(pukpin []byte) error {
// release releases resources associated with the channel.
func (s *Session) release() error {
return s.Wallet.card.Disconnect(scard.LeaveCard)
return s.Wallet.card.Disconnect(pcsc.LeaveCard)
}
// paired returns true if a valid pairing exists.

@ -118,9 +118,9 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
throwJSException(err.Error())
}
case strings.HasSuffix(err.Error(), scwallet.ErrPUKNeeded.Error()):
case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()):
// PUK input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil {
if input, err := b.prompter.PromptPassword("Please enter the pairing password: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)

@ -1,23 +0,0 @@
Copyright (c) 2016, Michael Gehring <mg@ebfe.org>
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.
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.

@ -1,14 +0,0 @@
scard
=====
[![GoDoc](https://godoc.org/github.com/ebfe/scard?status.svg)](https://godoc.org/github.com/ebfe/scard)
Go bindings to the PC/SC API.
## Installation
go get github.com/ebfe/scard
## Bugs
- Memory layouts/GC needs a thorough review.

@ -1,283 +0,0 @@
// Package scard provides bindings to the PC/SC API.
package scard
import (
"time"
"unsafe"
)
type CardStatus struct {
Reader string
State State
ActiveProtocol Protocol
Atr []byte
}
type ReaderState struct {
Reader string
UserData interface{}
CurrentState StateFlag
EventState StateFlag
Atr []byte
}
type Context struct {
ctx uintptr
}
type Card struct {
handle uintptr
activeProtocol Protocol
}
// wraps SCardEstablishContext
func EstablishContext() (*Context, error) {
ctx, r := scardEstablishContext(ScopeSystem, 0, 0)
if r != ErrSuccess {
return nil, r
}
return &Context{ctx: ctx}, nil
}
// wraps SCardIsValidContext
func (ctx *Context) IsValid() (bool, error) {
r := scardIsValidContext(ctx.ctx)
switch r {
case ErrSuccess:
return true, nil
case ErrInvalidHandle:
return false, nil
default:
return false, r
}
}
// wraps SCardCancel
func (ctx *Context) Cancel() error {
r := scardCancel(ctx.ctx)
if r != ErrSuccess {
return r
}
return nil
}
// wraps SCardReleaseContext
func (ctx *Context) Release() error {
r := scardReleaseContext(ctx.ctx)
if r != ErrSuccess {
return r
}
return nil
}
// wraps SCardListReaders
func (ctx *Context) ListReaders() ([]string, error) {
needed, r := scardListReaders(ctx.ctx, nil, nil, 0)
if r != ErrSuccess {
return nil, r
}
buf := make(strbuf, needed)
n, r := scardListReaders(ctx.ctx, nil, buf.ptr(), uint32(len(buf)))
if r != ErrSuccess {
return nil, r
}
return decodemstr(buf[:n]), nil
}
// wraps SCardListReaderGroups
func (ctx *Context) ListReaderGroups() ([]string, error) {
needed, r := scardListReaderGroups(ctx.ctx, nil, 0)
if r != ErrSuccess {
return nil, r
}
buf := make(strbuf, needed)
n, r := scardListReaderGroups(ctx.ctx, buf.ptr(), uint32(len(buf)))
if r != ErrSuccess {
return nil, r
}
return decodemstr(buf[:n]), nil
}
// wraps SCardGetStatusChange
func (ctx *Context) GetStatusChange(readerStates []ReaderState, timeout time.Duration) error {
dwTimeout := durationToTimeout(timeout)
states := make([]scardReaderState, len(readerStates))
for i := range readerStates {
var err error
states[i], err = readerStates[i].toSys()
if err != nil {
return err
}
}
r := scardGetStatusChange(ctx.ctx, dwTimeout, states)
if r != ErrSuccess {
return r
}
for i := range readerStates {
(&readerStates[i]).update(&states[i])
}
return nil
}
// wraps SCardConnect
func (ctx *Context) Connect(reader string, mode ShareMode, proto Protocol) (*Card, error) {
creader, err := encodestr(reader)
if err != nil {
return nil, err
}
handle, activeProtocol, r := scardConnect(ctx.ctx, creader.ptr(), mode, proto)
if r != ErrSuccess {
return nil, r
}
return &Card{handle: handle, activeProtocol: activeProtocol}, nil
}
// wraps SCardDisconnect
func (card *Card) Disconnect(d Disposition) error {
r := scardDisconnect(card.handle, d)
if r != ErrSuccess {
return r
}
return nil
}
// wraps SCardReconnect
func (card *Card) Reconnect(mode ShareMode, proto Protocol, disp Disposition) error {
activeProtocol, r := scardReconnect(card.handle, mode, proto, disp)
if r != ErrSuccess {
return r
}
card.activeProtocol = activeProtocol
return nil
}
// wraps SCardBeginTransaction
func (card *Card) BeginTransaction() error {
r := scardBeginTransaction(card.handle)
if r != ErrSuccess {
return r
}
return nil
}
// wraps SCardEndTransaction
func (card *Card) EndTransaction(disp Disposition) error {
r := scardEndTransaction(card.handle, disp)
if r != ErrSuccess {
return r
}
return nil
}
// wraps SCardStatus
func (card *Card) Status() (*CardStatus, error) {
reader, state, proto, atr, err := scardCardStatus(card.handle)
if err != ErrSuccess {
return nil, err
}
return &CardStatus{Reader: reader, State: state, ActiveProtocol: proto, Atr: atr}, nil
}
// wraps SCardTransmit
func (card *Card) Transmit(cmd []byte) ([]byte, error) {
rsp := make([]byte, maxBufferSizeExtended)
rspLen, err := scardTransmit(card.handle, card.activeProtocol, cmd, rsp)
if err != ErrSuccess {
return nil, err
}
return rsp[:rspLen], nil
}
// wraps SCardControl
func (card *Card) Control(ioctl uint32, in []byte) ([]byte, error) {
var out [0xffff]byte
outLen, err := scardControl(card.handle, ioctl, in, out[:])
if err != ErrSuccess {
return nil, err
}
return out[:outLen], nil
}
// wraps SCardGetAttrib
func (card *Card) GetAttrib(id Attrib) ([]byte, error) {
needed, err := scardGetAttrib(card.handle, id, nil)
if err != ErrSuccess {
return nil, err
}
var attrib = make([]byte, needed)
n, err := scardGetAttrib(card.handle, id, attrib)
if err != ErrSuccess {
return nil, err
}
return attrib[:n], nil
}
// wraps SCardSetAttrib
func (card *Card) SetAttrib(id Attrib, data []byte) error {
err := scardSetAttrib(card.handle, id, data)
if err != ErrSuccess {
return err
}
return nil
}
func durationToTimeout(timeout time.Duration) uint32 {
switch {
case timeout < 0:
return infiniteTimeout
case timeout > time.Duration(infiniteTimeout)*time.Millisecond:
return infiniteTimeout - 1
default:
return uint32(timeout / time.Millisecond)
}
}
func (buf strbuf) ptr() unsafe.Pointer {
return unsafe.Pointer(&buf[0])
}
func (buf strbuf) split() []strbuf {
var chunks []strbuf
for len(buf) > 0 && buf[0] != 0 {
i := 0
for i = range buf {
if buf[i] == 0 {
break
}
}
chunks = append(chunks, buf[:i+1])
buf = buf[i+1:]
}
return chunks
}
func encodemstr(strings ...string) (strbuf, error) {
var buf strbuf
for _, s := range strings {
utf16, err := encodestr(s)
if err != nil {
return nil, err
}
buf = append(buf, utf16...)
}
buf = append(buf, 0)
return buf, nil
}
func decodemstr(buf strbuf) []string {
var strings []string
for _, chunk := range buf.split() {
strings = append(strings, decodestr(chunk))
}
return strings
}

@ -1,219 +0,0 @@
// +build darwin
package scard
// #cgo LDFLAGS: -framework PCSC
// #cgo CFLAGS: -I /usr/include
// #include <stdlib.h>
// #include <PCSC/winscard.h>
// #include <PCSC/wintypes.h>
import "C"
import (
"unsafe"
)
func (e Error) Error() string {
return "scard: " + C.GoString(C.pcsc_stringify_error(C.int32_t(e)))
}
// Version returns the libpcsclite version string
func Version() string {
return C.PCSCLITE_VERSION_NUMBER
}
func scardEstablishContext(scope Scope, reserved1, reserved2 uintptr) (uintptr, Error) {
var ctx C.SCARDCONTEXT
r := C.SCardEstablishContext(C.uint32_t(scope), unsafe.Pointer(reserved1), unsafe.Pointer(reserved2), &ctx)
return uintptr(ctx), Error(r)
}
func scardIsValidContext(ctx uintptr) Error {
r := C.SCardIsValidContext(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardCancel(ctx uintptr) Error {
r := C.SCardCancel(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardReleaseContext(ctx uintptr) Error {
r := C.SCardReleaseContext(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardListReaders(ctx uintptr, groups, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := C.uint32_t(bufLen)
r := C.SCardListReaders(C.SCARDCONTEXT(ctx), (C.LPCSTR)(groups), (C.LPSTR)(buf), &dwBufLen)
return uint32(dwBufLen), Error(r)
}
func scardListReaderGroups(ctx uintptr, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := C.uint32_t(bufLen)
r := C.SCardListReaderGroups(C.SCARDCONTEXT(ctx), (C.LPSTR)(buf), &dwBufLen)
return uint32(dwBufLen), Error(r)
}
func scardGetStatusChange(ctx uintptr, timeout uint32, states []scardReaderState) Error {
// In darwin, the LPSCARD_READERSTATE_A has 1 byte alignment and hence
// has no trailing padding. Go does add 3 bytes of padding (on both 32
// and 64 bits), so we pack an array manually instead.
const size = int(unsafe.Sizeof(states[0])) - 3
buf := make([]byte, size*len(states))
for i, _ := range states {
copy(buf[i*size:(i+1)*size], (*(*[size]byte)(unsafe.Pointer(&states[i])))[:])
}
r := C.SCardGetStatusChange(C.SCARDCONTEXT(ctx), C.uint32_t(timeout), (C.LPSCARD_READERSTATE_A)(unsafe.Pointer(&buf[0])), C.uint32_t(len(states)))
for i, _ := range states {
copy((*(*[size]byte)(unsafe.Pointer(&states[i])))[:], buf[i*size:(i+1)*size])
}
return Error(r)
}
func scardConnect(ctx uintptr, reader unsafe.Pointer, shareMode ShareMode, proto Protocol) (uintptr, Protocol, Error) {
var handle C.SCARDHANDLE
var activeProto C.uint32_t
r := C.SCardConnect(C.SCARDCONTEXT(ctx), C.LPCSTR(reader), C.uint32_t(shareMode), C.uint32_t(proto), &handle, &activeProto)
return uintptr(handle), Protocol(activeProto), Error(r)
}
func scardDisconnect(card uintptr, d Disposition) Error {
r := C.SCardDisconnect(C.SCARDHANDLE(card), C.uint32_t(d))
return Error(r)
}
func scardReconnect(card uintptr, mode ShareMode, proto Protocol, disp Disposition) (Protocol, Error) {
var activeProtocol C.uint32_t
r := C.SCardReconnect(C.SCARDHANDLE(card), C.uint32_t(mode), C.uint32_t(proto), C.uint32_t(disp), &activeProtocol)
return Protocol(activeProtocol), Error(r)
}
func scardBeginTransaction(card uintptr) Error {
r := C.SCardBeginTransaction(C.SCARDHANDLE(card))
return Error(r)
}
func scardEndTransaction(card uintptr, disp Disposition) Error {
r := C.SCardEndTransaction(C.SCARDHANDLE(card), C.uint32_t(disp))
return Error(r)
}
func scardCardStatus(card uintptr) (string, State, Protocol, []byte, Error) {
var readerBuf [C.MAX_READERNAME + 1]byte
var readerLen = C.uint32_t(len(readerBuf))
var state, proto C.uint32_t
var atr [maxAtrSize]byte
var atrLen = C.uint32_t(len(atr))
r := C.SCardStatus(C.SCARDHANDLE(card), (C.LPSTR)(unsafe.Pointer(&readerBuf[0])), &readerLen, &state, &proto, (*C.uchar)(&atr[0]), &atrLen)
return decodestr(readerBuf[:readerLen]), State(state), Protocol(proto), atr[:atrLen], Error(r)
}
func scardTransmit(card uintptr, proto Protocol, cmd []byte, rsp []byte) (uint32, Error) {
var sendpci C.SCARD_IO_REQUEST
var recvpci C.SCARD_IO_REQUEST
var rspLen = C.uint32_t(len(rsp))
switch proto {
case ProtocolT0, ProtocolT1:
sendpci.dwProtocol = C.uint32_t(proto)
default:
panic("unknown protocol")
}
sendpci.cbPciLength = C.sizeof_SCARD_IO_REQUEST
r := C.SCardTransmit(C.SCARDHANDLE(card), &sendpci, (*C.uchar)(&cmd[0]), C.uint32_t(len(cmd)), &recvpci, (*C.uchar)(&rsp[0]), &rspLen)
return uint32(rspLen), Error(r)
}
func scardControl(card uintptr, ioctl uint32, in, out []byte) (uint32, Error) {
var ptrIn unsafe.Pointer
var outLen = C.uint32_t(len(out))
if len(in) != 0 {
ptrIn = unsafe.Pointer(&in[0])
}
r := C.SCardControl(C.SCARDHANDLE(card), C.uint32_t(ioctl), ptrIn, C.uint32_t(len(in)), unsafe.Pointer(&out[0]), C.uint32_t(len(out)), &outLen)
return uint32(outLen), Error(r)
}
func scardGetAttrib(card uintptr, id Attrib, buf []byte) (uint32, Error) {
var ptr *C.uint8_t
if len(buf) != 0 {
ptr = (*C.uint8_t)(&buf[0])
}
bufLen := C.uint32_t(len(buf))
r := C.SCardGetAttrib(C.SCARDHANDLE(card), C.uint32_t(id), ptr, &bufLen)
return uint32(bufLen), Error(r)
}
func scardSetAttrib(card uintptr, id Attrib, buf []byte) Error {
r := C.SCardSetAttrib(C.SCARDHANDLE(card), C.uint32_t(id), ((*C.uint8_t)(&buf[0])), C.uint32_t(len(buf)))
return Error(r)
}
type strbuf []byte
func encodestr(s string) (strbuf, error) {
buf := strbuf(s + "\x00")
return buf, nil
}
func decodestr(buf strbuf) string {
if len(buf) == 0 {
return ""
}
if buf[len(buf)-1] == 0 {
buf = buf[:len(buf)-1]
}
return string(buf)
}
type scardReaderState struct {
szReader uintptr
pvUserData uintptr
dwCurrentState uint32
dwEventState uint32
cbAtr uint32
rgbAtr [33]byte
}
var pinned = map[string]*strbuf{}
func (rs *ReaderState) toSys() (scardReaderState, error) {
var sys scardReaderState
creader, err := encodestr(rs.Reader)
if err != nil {
return scardReaderState{}, err
}
pinned[rs.Reader] = &creader
sys.szReader = uintptr(creader.ptr())
sys.dwCurrentState = uint32(rs.CurrentState)
sys.cbAtr = uint32(len(rs.Atr))
for i, v := range rs.Atr {
sys.rgbAtr[i] = byte(v)
}
return sys, nil
}
func (rs *ReaderState) update(sys *scardReaderState) {
rs.EventState = StateFlag(sys.dwEventState)
if sys.cbAtr > 0 {
rs.Atr = make([]byte, int(sys.cbAtr))
for i := 0; i < int(sys.cbAtr); i++ {
rs.Atr[i] = byte(sys.rgbAtr[i])
}
}
}

@ -1,206 +0,0 @@
// +build !windows,!darwin
package scard
// #cgo pkg-config: libpcsclite
// #include <stdlib.h>
// #include <winscard.h>
import "C"
import (
"unsafe"
)
func (e Error) Error() string {
return "scard: " + C.GoString(C.pcsc_stringify_error(C.LONG(e)))
}
// Version returns the libpcsclite version string
func Version() string {
return C.PCSCLITE_VERSION_NUMBER
}
func scardEstablishContext(scope Scope, reserved1, reserved2 uintptr) (uintptr, Error) {
var ctx C.SCARDCONTEXT
r := C.SCardEstablishContext(C.DWORD(scope), C.LPCVOID(reserved1), C.LPCVOID(reserved2), &ctx)
return uintptr(ctx), Error(r)
}
func scardIsValidContext(ctx uintptr) Error {
r := C.SCardIsValidContext(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardCancel(ctx uintptr) Error {
r := C.SCardCancel(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardReleaseContext(ctx uintptr) Error {
r := C.SCardReleaseContext(C.SCARDCONTEXT(ctx))
return Error(r)
}
func scardListReaders(ctx uintptr, groups, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := C.DWORD(bufLen)
r := C.SCardListReaders(C.SCARDCONTEXT(ctx), (C.LPCSTR)(groups), (C.LPSTR)(buf), &dwBufLen)
return uint32(dwBufLen), Error(r)
}
func scardListReaderGroups(ctx uintptr, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := C.DWORD(bufLen)
r := C.SCardListReaderGroups(C.SCARDCONTEXT(ctx), (C.LPSTR)(buf), &dwBufLen)
return uint32(dwBufLen), Error(r)
}
func scardGetStatusChange(ctx uintptr, timeout uint32, states []scardReaderState) Error {
r := C.SCardGetStatusChange(C.SCARDCONTEXT(ctx), C.DWORD(timeout), (C.LPSCARD_READERSTATE)(unsafe.Pointer(&states[0])), C.DWORD(len(states)))
return Error(r)
}
func scardConnect(ctx uintptr, reader unsafe.Pointer, shareMode ShareMode, proto Protocol) (uintptr, Protocol, Error) {
var handle C.SCARDHANDLE
var activeProto C.DWORD
r := C.SCardConnect(C.SCARDCONTEXT(ctx), C.LPCSTR(reader), C.DWORD(shareMode), C.DWORD(proto), &handle, &activeProto)
return uintptr(handle), Protocol(activeProto), Error(r)
}
func scardDisconnect(card uintptr, d Disposition) Error {
r := C.SCardDisconnect(C.SCARDHANDLE(card), C.DWORD(d))
return Error(r)
}
func scardReconnect(card uintptr, mode ShareMode, proto Protocol, disp Disposition) (Protocol, Error) {
var activeProtocol C.DWORD
r := C.SCardReconnect(C.SCARDHANDLE(card), C.DWORD(mode), C.DWORD(proto), C.DWORD(disp), &activeProtocol)
return Protocol(activeProtocol), Error(r)
}
func scardBeginTransaction(card uintptr) Error {
r := C.SCardBeginTransaction(C.SCARDHANDLE(card))
return Error(r)
}
func scardEndTransaction(card uintptr, disp Disposition) Error {
r := C.SCardEndTransaction(C.SCARDHANDLE(card), C.DWORD(disp))
return Error(r)
}
func scardCardStatus(card uintptr) (string, State, Protocol, []byte, Error) {
var readerBuf [C.MAX_READERNAME + 1]byte
var readerLen = C.DWORD(len(readerBuf))
var state, proto C.DWORD
var atr [maxAtrSize]byte
var atrLen = C.DWORD(len(atr))
r := C.SCardStatus(C.SCARDHANDLE(card), (C.LPSTR)(unsafe.Pointer(&readerBuf[0])), &readerLen, &state, &proto, (*C.BYTE)(&atr[0]), &atrLen)
return decodestr(readerBuf[:readerLen]), State(state), Protocol(proto), atr[:atrLen], Error(r)
}
func scardTransmit(card uintptr, proto Protocol, cmd []byte, rsp []byte) (uint32, Error) {
var sendpci C.SCARD_IO_REQUEST
var recvpci C.SCARD_IO_REQUEST
var rspLen = C.DWORD(len(rsp))
switch proto {
case ProtocolT0, ProtocolT1:
sendpci.dwProtocol = C.ulong(proto)
default:
panic("unknown protocol")
}
sendpci.cbPciLength = C.sizeof_SCARD_IO_REQUEST
r := C.SCardTransmit(C.SCARDHANDLE(card), &sendpci, (*C.BYTE)(&cmd[0]), C.DWORD(len(cmd)), &recvpci, (*C.BYTE)(&rsp[0]), &rspLen)
return uint32(rspLen), Error(r)
}
func scardControl(card uintptr, ioctl uint32, in, out []byte) (uint32, Error) {
var ptrIn C.LPCVOID
var outLen = C.DWORD(len(out))
if len(in) != 0 {
ptrIn = C.LPCVOID(unsafe.Pointer(&in[0]))
}
r := C.SCardControl(C.SCARDHANDLE(card), C.DWORD(ioctl), ptrIn, C.DWORD(len(in)), (C.LPVOID)(unsafe.Pointer(&out[0])), C.DWORD(len(out)), &outLen)
return uint32(outLen), Error(r)
}
func scardGetAttrib(card uintptr, id Attrib, buf []byte) (uint32, Error) {
var ptr C.LPBYTE
if len(buf) != 0 {
ptr = C.LPBYTE(unsafe.Pointer(&buf[0]))
}
bufLen := C.DWORD(len(buf))
r := C.SCardGetAttrib(C.SCARDHANDLE(card), C.DWORD(id), ptr, &bufLen)
return uint32(bufLen), Error(r)
}
func scardSetAttrib(card uintptr, id Attrib, buf []byte) Error {
r := C.SCardSetAttrib(C.SCARDHANDLE(card), C.DWORD(id), (*C.BYTE)(unsafe.Pointer(&buf[0])), C.DWORD(len(buf)))
return Error(r)
}
type strbuf []byte
func encodestr(s string) (strbuf, error) {
buf := strbuf(s + "\x00")
return buf, nil
}
func decodestr(buf strbuf) string {
if len(buf) == 0 {
return ""
}
if buf[len(buf)-1] == 0 {
buf = buf[:len(buf)-1]
}
return string(buf)
}
type scardReaderState struct {
szReader uintptr
pvUserData uintptr
dwCurrentState uintptr
dwEventState uintptr
cbAtr uintptr
rgbAtr [33]byte
}
var pinned = map[string]*strbuf{}
func (rs *ReaderState) toSys() (scardReaderState, error) {
var sys scardReaderState
creader, err := encodestr(rs.Reader)
if err != nil {
return scardReaderState{}, err
}
pinned[rs.Reader] = &creader
sys.szReader = uintptr(creader.ptr())
sys.dwCurrentState = uintptr(rs.CurrentState)
sys.cbAtr = uintptr(len(rs.Atr))
for i, v := range rs.Atr {
sys.rgbAtr[i] = byte(v)
}
return sys, nil
}
func (rs *ReaderState) update(sys *scardReaderState) {
rs.EventState = StateFlag(sys.dwEventState)
if sys.cbAtr > 0 {
rs.Atr = make([]byte, int(sys.cbAtr))
for i := 0; i < int(sys.cbAtr); i++ {
rs.Atr[i] = byte(sys.rgbAtr[i])
}
}
}

@ -1,221 +0,0 @@
package scard
import (
"fmt"
"syscall"
"unsafe"
)
var (
modwinscard = syscall.NewLazyDLL("winscard.dll")
procEstablishContext = modwinscard.NewProc("SCardEstablishContext")
procReleaseContext = modwinscard.NewProc("SCardReleaseContext")
procIsValidContext = modwinscard.NewProc("SCardIsValidContext")
procCancel = modwinscard.NewProc("SCardCancel")
procListReaders = modwinscard.NewProc("SCardListReadersW")
procListReaderGroups = modwinscard.NewProc("SCardListReaderGroupsW")
procGetStatusChange = modwinscard.NewProc("SCardGetStatusChangeW")
procConnect = modwinscard.NewProc("SCardConnectW")
procDisconnect = modwinscard.NewProc("SCardDisconnect")
procReconnect = modwinscard.NewProc("SCardReconnect")
procBeginTransaction = modwinscard.NewProc("SCardBeginTransaction")
procEndTransaction = modwinscard.NewProc("SCardEndTransaction")
procStatus = modwinscard.NewProc("SCardStatusW")
procTransmit = modwinscard.NewProc("SCardTransmit")
procControl = modwinscard.NewProc("SCardControl")
procGetAttrib = modwinscard.NewProc("SCardGetAttrib")
procSetAttrib = modwinscard.NewProc("SCardSetAttrib")
dataT0Pci = modwinscard.NewProc("g_rgSCardT0Pci")
dataT1Pci = modwinscard.NewProc("g_rgSCardT1Pci")
)
var scardIoReqT0 uintptr
var scardIoReqT1 uintptr
func init() {
if err := dataT0Pci.Find(); err != nil {
panic(err)
}
scardIoReqT0 = dataT0Pci.Addr()
if err := dataT1Pci.Find(); err != nil {
panic(err)
}
scardIoReqT1 = dataT1Pci.Addr()
}
func (e Error) Error() string {
err := syscall.Errno(e)
return fmt.Sprintf("scard: error(%x): %s", uintptr(e), err.Error())
}
func scardEstablishContext(scope Scope, reserved1, reserved2 uintptr) (uintptr, Error) {
var ctx uintptr
r, _, _ := procEstablishContext.Call(uintptr(scope), reserved1, reserved2, uintptr(unsafe.Pointer(&ctx)))
return ctx, Error(r)
}
func scardIsValidContext(ctx uintptr) Error {
r, _, _ := procIsValidContext.Call(ctx)
return Error(r)
}
func scardCancel(ctx uintptr) Error {
r, _, _ := procCancel.Call(ctx)
return Error(r)
}
func scardReleaseContext(ctx uintptr) Error {
r, _, _ := procReleaseContext.Call(ctx)
return Error(r)
}
func scardListReaders(ctx uintptr, groups, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := uint32(bufLen)
r, _, _ := procListReaders.Call(ctx, uintptr(groups), uintptr(buf), uintptr(unsafe.Pointer(&dwBufLen)))
return dwBufLen, Error(r)
}
func scardListReaderGroups(ctx uintptr, buf unsafe.Pointer, bufLen uint32) (uint32, Error) {
dwBufLen := uint32(bufLen)
r, _, _ := procListReaderGroups.Call(ctx, uintptr(buf), uintptr(unsafe.Pointer(&dwBufLen)))
return dwBufLen, Error(r)
}
func scardGetStatusChange(ctx uintptr, timeout uint32, states []scardReaderState) Error {
r, _, _ := procGetStatusChange.Call(ctx, uintptr(timeout), uintptr(unsafe.Pointer(&states[0])), uintptr(len(states)))
return Error(r)
}
func scardConnect(ctx uintptr, reader unsafe.Pointer, shareMode ShareMode, proto Protocol) (uintptr, Protocol, Error) {
var handle uintptr
var activeProto uint32
r, _, _ := procConnect.Call(ctx, uintptr(reader), uintptr(shareMode), uintptr(proto), uintptr(unsafe.Pointer(&handle)), uintptr(unsafe.Pointer(&activeProto)))
return handle, Protocol(activeProto), Error(r)
}
func scardDisconnect(card uintptr, d Disposition) Error {
r, _, _ := procDisconnect.Call(card, uintptr(d))
return Error(r)
}
func scardReconnect(card uintptr, mode ShareMode, proto Protocol, disp Disposition) (Protocol, Error) {
var activeProtocol uint32
r, _, _ := procReconnect.Call(card, uintptr(mode), uintptr(proto), uintptr(disp), uintptr(unsafe.Pointer(&activeProtocol)))
return Protocol(activeProtocol), Error(r)
}
func scardBeginTransaction(card uintptr) Error {
r, _, _ := procBeginTransaction.Call(card)
return Error(r)
}
func scardEndTransaction(card uintptr, disp Disposition) Error {
r, _, _ := procEndTransaction.Call(card, uintptr(disp))
return Error(r)
}
func scardCardStatus(card uintptr) (string, State, Protocol, []byte, Error) {
var state, proto uint32
var atr [maxAtrSize]byte
var atrLen = uint32(len(atr))
reader := make(strbuf, maxReadername+1)
readerLen := uint32(len(reader))
r, _, _ := procStatus.Call(card, uintptr(reader.ptr()), uintptr(unsafe.Pointer(&readerLen)), uintptr(unsafe.Pointer(&state)), uintptr(unsafe.Pointer(&proto)), uintptr(unsafe.Pointer(&atr[0])), uintptr(unsafe.Pointer(&atrLen)))
return decodestr(reader[:readerLen]), State(state), Protocol(proto), atr[:atrLen], Error(r)
}
func scardTransmit(card uintptr, proto Protocol, cmd []byte, rsp []byte) (uint32, Error) {
var sendpci uintptr
var rspLen = uint32(len(rsp))
switch proto {
case ProtocolT0:
sendpci = scardIoReqT0
case ProtocolT1:
sendpci = scardIoReqT1
default:
panic("unknown protocol")
}
r, _, _ := procTransmit.Call(card, sendpci, uintptr(unsafe.Pointer(&cmd[0])), uintptr(len(cmd)), uintptr(0), uintptr(unsafe.Pointer(&rsp[0])), uintptr(unsafe.Pointer(&rspLen)))
return rspLen, Error(r)
}
func scardControl(card uintptr, ioctl uint32, in, out []byte) (uint32, Error) {
var ptrIn uintptr
var outLen = uint32(len(out))
if len(in) != 0 {
ptrIn = uintptr(unsafe.Pointer(&in[0]))
}
r, _, _ := procControl.Call(card, uintptr(ioctl), ptrIn, uintptr(len(in)), uintptr(unsafe.Pointer(&out[0])), uintptr(len(out)), uintptr(unsafe.Pointer(&outLen)))
return outLen, Error(r)
}
func scardGetAttrib(card uintptr, id Attrib, buf []byte) (uint32, Error) {
var ptr uintptr
if len(buf) != 0 {
ptr = uintptr(unsafe.Pointer(&buf[0]))
}
bufLen := uint32(len(buf))
r, _, _ := procGetAttrib.Call(card, uintptr(id), ptr, uintptr(unsafe.Pointer(&bufLen)))
return bufLen, Error(r)
}
func scardSetAttrib(card uintptr, id Attrib, buf []byte) Error {
r, _, _ := procSetAttrib.Call(card, uintptr(id), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
return Error(r)
}
type scardReaderState struct {
szReader uintptr
pvUserData uintptr
dwCurrentState uint32
dwEventState uint32
cbAtr uint32
rgbAtr [36]byte
}
func (rs *ReaderState) toSys() (scardReaderState, error) {
var sys scardReaderState
creader, err := encodestr(rs.Reader)
if err != nil {
return scardReaderState{}, err
}
sys.szReader = uintptr(creader.ptr())
sys.dwCurrentState = uint32(rs.CurrentState)
sys.cbAtr = uint32(len(rs.Atr))
copy(sys.rgbAtr[:], rs.Atr)
return sys, nil
}
func (rs *ReaderState) update(sys *scardReaderState) {
rs.EventState = StateFlag(sys.dwEventState)
if sys.cbAtr > 0 {
rs.Atr = make([]byte, int(sys.cbAtr))
copy(rs.Atr, sys.rgbAtr[:])
}
}
type strbuf []uint16
func encodestr(s string) (strbuf, error) {
utf16, err := syscall.UTF16FromString(s)
return strbuf(utf16), err
}
func decodestr(buf strbuf) string {
return syscall.UTF16ToString(buf)
}

@ -1,190 +0,0 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs -- -I /usr/include/PCSC/ const.go
package scard
type Attrib uint32
const (
AttrVendorName Attrib = 0x10100
AttrVendorIfdType Attrib = 0x10101
AttrVendorIfdVersion Attrib = 0x10102
AttrVendorIfdSerialNo Attrib = 0x10103
AttrChannelId Attrib = 0x20110
AttrAsyncProtocolTypes Attrib = 0x30120
AttrDefaultClk Attrib = 0x30121
AttrMaxClk Attrib = 0x30122
AttrDefaultDataRate Attrib = 0x30123
AttrMaxDataRate Attrib = 0x30124
AttrMaxIfsd Attrib = 0x30125
AttrSyncProtocolTypes Attrib = 0x30126
AttrPowerMgmtSupport Attrib = 0x40131
AttrUserToCardAuthDevice Attrib = 0x50140
AttrUserAuthInputDevice Attrib = 0x50142
AttrCharacteristics Attrib = 0x60150
AttrCurrentProtocolType Attrib = 0x80201
AttrCurrentClk Attrib = 0x80202
AttrCurrentF Attrib = 0x80203
AttrCurrentD Attrib = 0x80204
AttrCurrentN Attrib = 0x80205
AttrCurrentW Attrib = 0x80206
AttrCurrentIfsc Attrib = 0x80207
AttrCurrentIfsd Attrib = 0x80208
AttrCurrentBwt Attrib = 0x80209
AttrCurrentCwt Attrib = 0x8020a
AttrCurrentEbcEncoding Attrib = 0x8020b
AttrExtendedBwt Attrib = 0x8020c
AttrIccPresence Attrib = 0x90300
AttrIccInterfaceStatus Attrib = 0x90301
AttrCurrentIoState Attrib = 0x90302
AttrAtrString Attrib = 0x90303
AttrIccTypePerAtr Attrib = 0x90304
AttrEscReset Attrib = 0x7a000
AttrEscCancel Attrib = 0x7a003
AttrEscAuthrequest Attrib = 0x7a005
AttrMaxinput Attrib = 0x7a007
AttrDeviceUnit Attrib = 0x7fff0001
AttrDeviceInUse Attrib = 0x7fff0002
AttrDeviceFriendlyName Attrib = 0x7fff0003
AttrDeviceSystemName Attrib = 0x7fff0004
AttrSupressT1IfsRequest Attrib = 0x7fff0007
)
type Error uint32
const (
ErrSuccess Error = 0x0
ErrInternalError Error = 0x80100001
ErrCancelled Error = 0x80100002
ErrInvalidHandle Error = 0x80100003
ErrInvalidParameter Error = 0x80100004
ErrInvalidTarget Error = 0x80100005
ErrNoMemory Error = 0x80100006
ErrWaitedTooLong Error = 0x80100007
ErrInsufficientBuffer Error = 0x80100008
ErrUnknownReader Error = 0x80100009
ErrTimeout Error = 0x8010000a
ErrSharingViolation Error = 0x8010000b
ErrNoSmartcard Error = 0x8010000c
ErrUnknownCard Error = 0x8010000d
ErrCantDispose Error = 0x8010000e
ErrProtoMismatch Error = 0x8010000f
ErrNotReady Error = 0x80100010
ErrInvalidValue Error = 0x80100011
ErrSystemCancelled Error = 0x80100012
ErrCommError Error = 0x80100013
ErrUnknownError Error = 0x80100014
ErrInvalidAtr Error = 0x80100015
ErrNotTransacted Error = 0x80100016
ErrReaderUnavailable Error = 0x80100017
ErrShutdown Error = 0x80100018
ErrPciTooSmall Error = 0x80100019
ErrReaderUnsupported Error = 0x8010001a
ErrDuplicateReader Error = 0x8010001b
ErrCardUnsupported Error = 0x8010001c
ErrNoService Error = 0x8010001d
ErrServiceStopped Error = 0x8010001e
ErrUnexpected Error = 0x8010001f
ErrUnsupportedFeature Error = 0x8010001f
ErrIccInstallation Error = 0x80100020
ErrIccCreateorder Error = 0x80100021
ErrFileNotFound Error = 0x80100024
ErrNoDir Error = 0x80100025
ErrNoFile Error = 0x80100026
ErrNoAccess Error = 0x80100027
ErrWriteTooMany Error = 0x80100028
ErrBadSeek Error = 0x80100029
ErrInvalidChv Error = 0x8010002a
ErrUnknownResMng Error = 0x8010002b
ErrNoSuchCertificate Error = 0x8010002c
ErrCertificateUnavailable Error = 0x8010002d
ErrNoReadersAvailable Error = 0x8010002e
ErrCommDataLost Error = 0x8010002f
ErrNoKeyContainer Error = 0x80100030
ErrServerTooBusy Error = 0x80100031
ErrUnsupportedCard Error = 0x80100065
ErrUnresponsiveCard Error = 0x80100066
ErrUnpoweredCard Error = 0x80100067
ErrResetCard Error = 0x80100068
ErrRemovedCard Error = 0x80100069
ErrSecurityViolation Error = 0x8010006a
ErrWrongChv Error = 0x8010006b
ErrChvBlocked Error = 0x8010006c
ErrEof Error = 0x8010006d
ErrCancelledByUser Error = 0x8010006e
ErrCardNotAuthenticated Error = 0x8010006f
)
type Protocol uint32
const (
ProtocolUndefined Protocol = 0x0
ProtocolT0 Protocol = 0x1
ProtocolT1 Protocol = 0x2
ProtocolAny Protocol = ProtocolT0 | ProtocolT1
)
type ShareMode uint32
const (
ShareExclusive ShareMode = 0x1
ShareShared ShareMode = 0x2
ShareDirect ShareMode = 0x3
)
type Disposition uint32
const (
LeaveCard Disposition = 0x0
ResetCard Disposition = 0x1
UnpowerCard Disposition = 0x2
EjectCard Disposition = 0x3
)
type Scope uint32
const (
ScopeUser Scope = 0x0
ScopeTerminal Scope = 0x1
ScopeSystem Scope = 0x2
)
type State uint32
const (
Unknown State = 0x1
Absent State = 0x2
Present State = 0x4
Swallowed State = 0x8
Powered State = 0x10
Negotiable State = 0x20
Specific State = 0x40
)
type StateFlag uint32
const (
StateUnaware StateFlag = 0x0
StateIgnore StateFlag = 0x1
StateChanged StateFlag = 0x2
StateUnknown StateFlag = 0x4
StateUnavailable StateFlag = 0x8
StateEmpty StateFlag = 0x10
StatePresent StateFlag = 0x20
StateAtrmatch StateFlag = 0x40
StateExclusive StateFlag = 0x80
StateInuse StateFlag = 0x100
StateMute StateFlag = 0x200
StateUnpowered StateFlag = 0x400
)
const (
maxBufferSize = 0x108
maxBufferSizeExtended = 0x1000c
maxReadername = 0x80
maxAtrSize = 0x21
)
const (
infiniteTimeout = 0xffffffff
)

@ -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.

@ -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.

@ -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 */
)

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

@ -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)
}

@ -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
}

@ -145,6 +145,12 @@
"revision": "ca190fb6ffbc076ff49197b7168a760f30182d2e",
"revisionTime": "2018-04-18T12:24:29Z"
},
{
"checksumSHA1": "GnNfMrYs/4m+HCtDBF7HpPUFFVw=",
"path": "github.com/gballet/go-libpcsclite",
"revision": "95b81846253cd854b8bb8f2fd9cc6056d0681ac4",
"revisionTime": "2019-03-13T11:40:44Z"
},
{
"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=",
"path": "github.com/go-ole/go-ole",

Loading…
Cancel
Save