keycard-go/vendor/github.com/ebfe/scard/scard.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
}