301 lines
6.2 KiB
Go
301 lines
6.2 KiB
Go
// 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) {
|
|
|
|
var readerBuf = make(strbuf, maxReadername+1)
|
|
var atrBuf = make([]byte, maxAtrSize)
|
|
|
|
readerLen, state, proto, atrLen, err := scardCardStatus(card.handle, readerBuf, atrBuf)
|
|
if err == ErrInsufficientBuffer {
|
|
if uint32(len(readerBuf)) < readerLen {
|
|
readerBuf = make(strbuf, readerLen)
|
|
}
|
|
if uint32(len(atrBuf)) < atrLen {
|
|
atrBuf = make([]byte, atrLen)
|
|
}
|
|
readerLen, state, proto, atrLen, err = scardCardStatus(card.handle, readerBuf, atrBuf)
|
|
}
|
|
|
|
if err != ErrSuccess {
|
|
return nil, err
|
|
}
|
|
|
|
reader := decodemstr(readerBuf[:readerLen])
|
|
|
|
return &CardStatus{Reader: reader[0], State: state, ActiveProtocol: proto, Atr: atrBuf[:atrLen]}, 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
|
|
}
|