├── LICENSE ├── README.md ├── examples ├── apdu │ └── main.go └── show-atr │ └── main.go ├── go.mod └── smartcard ├── SW └── sw.go ├── pcsc ├── constants.go ├── error.go ├── pcsclite_client.go ├── pcsclite_client_test.go └── winscard_wrapper.go ├── smartcard.go ├── smartcard_pcsclite.go ├── smartcard_test.go └── smartcard_winscard.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Sebastian Fleissner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **This repository is currently not maintained** 2 | 3 | Go Smart Card API 4 | ================= 5 | 6 | High level API for smart card integration written in pure Go. On Linx and other 7 | Unix-like systems, this package talks directly to the PCSC-Lite daemon, and on 8 | Windows it accesses the smart card service via winscard.dll. 9 | 10 | **Note**: macOS isn't and won't be supported, because its modified PCSC-Lite 11 | variant can't be accessed without `cgo`. PCSC-Lite will most likely be (already 12 | is?) deprecated in favour of `CryptoTokenKit`. 13 | 14 | Prerequisites 15 | ------------- 16 | 17 | ### Linux 18 | 19 | Install the PCSC-Lite daemon and CCID driver. 20 | 21 | Ubuntu: 22 | 23 | sudo apt-get install pcscd libccid 24 | 25 | Arch Linux: 26 | 27 | sudo pacman -S pcsclite ccid 28 | sudo systemctl enable pcscd 29 | 30 | ### Windows 31 | 32 | None 33 | 34 | Usage 35 | ----- 36 | 37 | import ( 38 | "fmt" 39 | "github.com/sf1/go-card/smartcard" 40 | ) 41 | 42 | ctx, err := smartcard.EstablishContext() 43 | // handle error, if any 44 | defer ctx.Release() 45 | 46 | reader, err := ctx.WaitForCardPresent() 47 | // handle error, if any 48 | 49 | card, err := reader.Connect() 50 | // handle error, if any 51 | defer card.Disconnect() 52 | 53 | fmt.Printf("Card ATR: %s\n", card.ATR()) 54 | command := SelectCommand(0xa0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0xc, 0x01, 0x01) 55 | response, err := card.TransmitAPDU(command) 56 | // handle error, if any 57 | fmt.Printf("Response: %s\n", response) 58 | 59 | License 60 | ------- 61 | 62 | MIT. See [LICENSE](LICENSE) 63 | -------------------------------------------------------------------------------- /examples/apdu/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "flag" 7 | "bufio" 8 | "encoding/hex" 9 | "strings" 10 | "github.com/sf1/go-card/smartcard" 11 | ) 12 | 13 | func main() { 14 | var script, aid string 15 | flag.StringVar(&script, "script", "", "script") 16 | flag.StringVar(&aid, "aid", "", "applet id") 17 | flag.Usage = func() { 18 | fmt.Println("\nusage: apdu [ -aid -script ]\n") 19 | } 20 | flag.Parse() 21 | err := run(aid, script) 22 | if err != nil { 23 | fmt.Printf("\nerror: %s\n\n", err) 24 | os.Exit(1) 25 | } 26 | } 27 | 28 | func run(aid, script string) error { 29 | var reader *smartcard.Reader 30 | ctx, err := smartcard.EstablishContext() 31 | if err != nil { 32 | return err 33 | } 34 | defer ctx.Release() 35 | readers, err := ctx.ListReadersWithCard() 36 | if err != nil { 37 | return err 38 | } 39 | if len(readers) == 0 { 40 | fmt.Println("\nplease insert smart card\n") 41 | return nil 42 | } 43 | if len(readers) == 1 { 44 | reader = readers[0] 45 | } else { 46 | // to do: handle multiple readers choices 47 | return fmt.Errorf("multiple readers not supported") 48 | } 49 | card, err := reader.Connect() 50 | if err != nil { 51 | return err 52 | } 53 | defer card.Disconnect() 54 | if aid != "" { 55 | fmt.Println("") 56 | err = processCommand(card, "select " + aid, true) 57 | if err != nil { 58 | return err 59 | } 60 | } 61 | if script == "" { 62 | return runInteractive(card) 63 | } 64 | return runScript(card, script) 65 | } 66 | 67 | func runInteractive(card *smartcard.Card) error { 68 | scanner := bufio.NewScanner(os.Stdin) 69 | for { 70 | fmt.Print("\n>> ") 71 | scanner.Scan() 72 | command := scanner.Text() 73 | if command == "" { 74 | break 75 | } 76 | err := processCommand(card, command, false) 77 | if err != nil { 78 | fmt.Printf("error: %s\n", err) 79 | continue 80 | } 81 | } 82 | fmt.Println("") 83 | return nil 84 | } 85 | 86 | func runScript(card *smartcard.Card, script string) error { 87 | file, err := os.Open(script) 88 | if err != nil { 89 | return err 90 | } 91 | defer file.Close() 92 | scanner := bufio.NewScanner(file) 93 | for scanner.Scan() { 94 | fmt.Println("") 95 | err := processCommand(card, scanner.Text(), true) 96 | if err != nil { 97 | fmt.Printf("error: %s\n", err) 98 | continue 99 | } 100 | } 101 | fmt.Println("") 102 | return nil 103 | } 104 | 105 | func processCommand(card *smartcard.Card, command string, echoCmd bool) error { 106 | apdu := make([]byte, 0, 128) 107 | parts := strings.Split(command, " ") 108 | isSelect := false 109 | if strings.ToLower(parts[0]) == "select" { 110 | isSelect = true 111 | parts = parts[1:] 112 | apdu = append(apdu, 0x00, 0xa4, 0x04, 0x00, 0x00) 113 | } 114 | for _, p := range parts { 115 | bytes, err := hex.DecodeString(p) 116 | if err != nil { 117 | return err 118 | } 119 | apdu = append(apdu, bytes...) 120 | } 121 | if isSelect { 122 | if len(apdu) == 5 { 123 | return fmt.Errorf("no aid provided") 124 | } 125 | apdu[4] = byte(len(apdu) - 5) 126 | } 127 | cmd := smartcard.CommandAPDU(apdu) 128 | if echoCmd { 129 | fmt.Printf(">> %s\n", cmd) 130 | } 131 | res, err := card.TransmitAPDU(cmd) 132 | if err != nil { 133 | return err 134 | } 135 | fmt.Printf("<< %s\n", res) 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /examples/show-atr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/sf1/go-card/smartcard" 6 | ) 7 | 8 | func main() { 9 | ctx, err := smartcard.EstablishContext() 10 | if err != nil { panic(err) } 11 | defer ctx.Release() 12 | fmt.Printf("\nWaiting for card...") 13 | reader, err := ctx.WaitForCardPresent() 14 | if err != nil { panic(err) } 15 | card, err := reader.Connect() 16 | if err != nil { panic(err) } 17 | fmt.Printf("\n\nATR: %s\n\n",card.ATR()) 18 | card.Disconnect() 19 | fmt.Printf("Please remove card"); 20 | reader.WaitUntilCardRemoved() 21 | fmt.Printf("\n\n") 22 | } 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sf1/go-card 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /smartcard/SW/sw.go: -------------------------------------------------------------------------------- 1 | package SW 2 | 3 | const ( 4 | AUTH_FAILED uint16 = 0x63c0 5 | WRONG_LENGTH uint16 = 0x6700 6 | RECORD_NOT_FOUND uint16 = 0x6a83 7 | UNSUPPORTED_INS uint16 = 0x6d00 8 | UNSUPPORTED_CLA uint16 = 0x6e00 9 | EXCEPTION uint16 = 0x6f00 10 | SUCCESS uint16 = 0x9000 11 | NOT_AUTHORIZED uint16 = 0x91ae 12 | INSUFFICIENT_MEMORY uint16 = 0x9210 13 | ) 14 | -------------------------------------------------------------------------------- /smartcard/pcsc/constants.go: -------------------------------------------------------------------------------- 1 | package pcsc 2 | 3 | const ( 4 | // Scope 5 | CARD_SCOPE_USER = 0x0000 6 | CARD_SCOPE_TERMINAL = 0x0001 7 | CARD_SCOPE_SYSTEM = 0x0002 8 | // Limits 9 | _MAX_ATR_SIZE = 33 10 | // States (Internal) 11 | SCARD_UNKNOWN = 0x0001 12 | SCARD_ABSENT = 0x0002 13 | SCARD_PRESENT = 0x0004 14 | SCARD_SWALLOWED = 0x0008 15 | SCARD_POWERED = 0x0010 16 | SCARD_NEGOTIABLE = 0x0020 17 | SCARD_SPECIFIC = 0x0040 18 | // States (GetStatusChange) 19 | SCARD_STATE_UNAWARE = 0x0000 20 | SCARD_STATE_IGNORE = 0x0001 21 | SCARD_STATE_CHANGED = 0x0002 22 | SCARD_STATE_UNKNOWN = 0x0004 23 | SCARD_STATE_UNAVAILABLE = 0x0008 24 | SCARD_STATE_EMPTY = 0x0010 25 | SCARD_STATE_PRESENT = 0x0020 26 | SCARD_STATE_ATRMATCH = 0x0040 27 | SCARD_STATE_EXCLUSIVE = 0x0080 28 | SCARD_STATE_INUSE = 0x0100 29 | SCARD_STATE_MUTE = 0x0200 30 | SCARD_STATE_UNPOWERED = 0x0400 31 | // Protocols 32 | SCARD_PROTOCOL_UNDEFINED = 0x0000 33 | SCARD_PROTOCOL_T0 = 0x0001 34 | SCARD_PROTOCOL_T1 = 0x0002 35 | SCARD_PROTOCOL_RAW = 0x0004 36 | SCARD_PROTOCOL_T15 = 0x0008 37 | SCARD_PROTOCOL_ANY = (SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1) 38 | // Sharing 39 | SCARD_SHARE_EXCLUSIVE = 0x0001 40 | SCARD_SHARE_SHARED = 0x0002 41 | SCARD_SHARE_DIRECT = 0x0003 42 | // Disconnect 43 | SCARD_LEAVE_CARD = 0x0000 44 | SCARD_RESET_CARD = 0x0001 45 | SCARD_UNPOWER_CARD = 0x0002 46 | SCARD_EJECT_CARD = 0x0003 47 | // Errors 48 | SCARD_S_SUCCESS = 0x00000000 49 | SCARD_F_INTERNAL_ERROR = 0x80100001 50 | SCARD_E_CANCELLED = 0x80100002 51 | SCARD_E_INVALID_HANDLE = 0x80100003 52 | SCARD_E_INVALID_PARAMETER = 0x80100004 53 | SCARD_E_INVALID_TARGET = 0x80100005 54 | SCARD_E_NO_MEMORY = 0x80100006 55 | SCARD_F_WAITED_TOO_LONG = 0x80100007 56 | SCARD_E_INSUFFICIENT_BUFFER = 0x80100008 57 | SCARD_E_UNKNOWN_READER = 0x80100009 58 | SCARD_E_TIMEOUT = 0x8010000A 59 | SCARD_E_SHARING_VIOLATION = 0x8010000B 60 | SCARD_E_NO_SMARTCARD = 0x8010000C 61 | SCARD_E_UNKNOWN_CARD = 0x8010000D 62 | SCARD_E_CANT_DISPOSE = 0x8010000E 63 | SCARD_E_PROTO_MISMATCH = 0x8010000F 64 | SCARD_E_NOT_READY = 0x80100010 65 | SCARD_E_INVALID_VALUE = 0x80100011 66 | SCARD_E_SYSTEM_CANCELLED = 0x80100012 67 | SCARD_F_COMM_ERROR = 0x80100013 68 | SCARD_F_UNKNOWN_ERROR = 0x80100014 69 | SCARD_E_INVALID_ATR = 0x80100015 70 | SCARD_E_NOT_TRANSACTED = 0x80100016 71 | SCARD_E_READER_UNAVAILABLE = 0x80100017 72 | SCARD_P_SHUTDOWN = 0x80100018 73 | SCARD_E_PCI_TOO_SMALL = 0x80100019 74 | SCARD_E_READER_UNSUPPORTED = 0x8010001A 75 | SCARD_E_DUPLICATE_READER = 0x8010001B 76 | SCARD_E_CARD_UNSUPPORTED = 0x8010001C 77 | SCARD_E_NO_SERVICE = 0x8010001D 78 | SCARD_E_SERVICE_STOPPED = 0x8010001E 79 | SCARD_E_UNEXPECTED = 0x8010001F 80 | SCARD_E_ICC_INSTALLATION = 0x80100020 81 | SCARD_E_ICC_CREATEORDER = 0x80100021 82 | SCARD_E_UNSUPPORTED_FEATURE = 0x80100022 83 | SCARD_E_DIR_NOT_FOUND = 0x80100023 84 | SCARD_E_FILE_NOT_FOUND = 0x80100024 85 | SCARD_E_NO_DIR = 0x80100025 86 | SCARD_E_NO_FILE = 0x80100026 87 | SCARD_E_NO_ACCESS = 0x80100027 88 | SCARD_E_WRITE_TOO_MANY = 0x80100028 89 | SCARD_E_BAD_SEEK = 0x80100029 90 | SCARD_E_INVALID_CHV = 0x8010002A 91 | SCARD_E_UNKNOWN_RES_MNG = 0x8010002B 92 | SCARD_E_NO_SUCH_CERTIFICATE = 0x8010002C 93 | SCARD_E_CERTIFICATE_UNAVAILABLE = 0x8010002D 94 | SCARD_E_NO_READERS_AVAILABLE = 0x8010002E 95 | SCARD_E_COMM_DATA_LOST = 0x8010002F 96 | SCARD_E_NO_KEY_CONTAINER = 0x80100030 97 | SCARD_E_SERVER_TOO_BUSY = 0x80100031 98 | SCARD_W_UNSUPPORTED_CARD = 0x80100065 99 | SCARD_W_UNRESPONSIVE_CARD = 0x80100066 100 | SCARD_W_UNPOWERED_CARD = 0x80100067 101 | SCARD_W_RESET_CARD = 0x80100068 102 | SCARD_W_REMOVED_CARD = 0x80100069 103 | SCARD_W_SECURITY_VIOLATION = 0x8010006A 104 | SCARD_W_WRONG_CHV = 0x8010006B 105 | SCARD_W_CHV_BLOCKED = 0x8010006C 106 | SCARD_W_EOF = 0x8010006D 107 | SCARD_W_CANCELLED_BY_USER = 0x8010006E 108 | SCARD_W_CARD_NOT_AUTHENTICATED = 0x8010006F 109 | // Others 110 | SCARD_INFINITE = 0xFFFFFFFF 111 | // Attributes 112 | SCARD_CLASS_ICC_STATE = 9 113 | SCARD_ATTR_ATR_STRING = (SCARD_CLASS_ICC_STATE << 16 | 0x0303) 114 | ) 115 | -------------------------------------------------------------------------------- /smartcard/pcsc/error.go: -------------------------------------------------------------------------------- 1 | package pcsc 2 | 3 | func errorString(code uint32) string { 4 | str := "Unknown error" 5 | switch code { 6 | case SCARD_F_INTERNAL_ERROR: 7 | str ="SCARD_F_INTERNAL_ERROR" 8 | case SCARD_E_CANCELLED: 9 | str ="SCARD_E_CANCELLED" 10 | case SCARD_E_INVALID_HANDLE: 11 | str ="SCARD_E_INVALID_HANDLE" 12 | case SCARD_E_INVALID_PARAMETER: 13 | str ="SCARD_E_INVALID_PARAMETER" 14 | case SCARD_E_INVALID_TARGET: 15 | str ="SCARD_E_INVALID_TARGET" 16 | case SCARD_E_NO_MEMORY: 17 | str ="SCARD_E_NO_MEMORY" 18 | case SCARD_F_WAITED_TOO_LONG: 19 | str ="SCARD_F_WAITED_TOO_LONG" 20 | case SCARD_E_INSUFFICIENT_BUFFER: 21 | str ="SCARD_E_INSUFFICIENT_BUFFER" 22 | case SCARD_E_UNKNOWN_READER: 23 | str ="SCARD_E_UNKNOWN_READER" 24 | case SCARD_E_TIMEOUT: 25 | str ="SCARD_E_TIMEOUT" 26 | case SCARD_E_SHARING_VIOLATION: 27 | str ="SCARD_E_SHARING_VIOLATION" 28 | case SCARD_E_NO_SMARTCARD: 29 | str ="SCARD_E_NO_SMARTCARD" 30 | case SCARD_E_UNKNOWN_CARD: 31 | str ="SCARD_E_UNKNOWN_CARD" 32 | case SCARD_E_CANT_DISPOSE: 33 | str ="SCARD_E_CANT_DISPOSE" 34 | case SCARD_E_PROTO_MISMATCH: 35 | str ="SCARD_E_PROTO_MISMATCH" 36 | case SCARD_E_NOT_READY: 37 | str ="SCARD_E_NOT_READY" 38 | case SCARD_E_INVALID_VALUE: 39 | str ="SCARD_E_INVALID_VALUE" 40 | case SCARD_E_SYSTEM_CANCELLED: 41 | str ="SCARD_E_SYSTEM_CANCELLED" 42 | case SCARD_F_COMM_ERROR: 43 | str ="SCARD_F_COMM_ERROR" 44 | case SCARD_F_UNKNOWN_ERROR: 45 | str ="SCARD_F_UNKNOWN_ERROR" 46 | case SCARD_E_INVALID_ATR: 47 | str ="SCARD_E_INVALID_ATR" 48 | case SCARD_E_NOT_TRANSACTED: 49 | str ="SCARD_E_NOT_TRANSACTED" 50 | case SCARD_E_READER_UNAVAILABLE: 51 | str ="SCARD_E_READER_UNAVAILABLE" 52 | case SCARD_P_SHUTDOWN: 53 | str ="SCARD_P_SHUTDOWN" 54 | case SCARD_E_PCI_TOO_SMALL: 55 | str ="SCARD_E_PCI_TOO_SMALL" 56 | case SCARD_E_READER_UNSUPPORTED: 57 | str ="SCARD_E_READER_UNSUPPORTED" 58 | case SCARD_E_DUPLICATE_READER: 59 | str ="SCARD_E_DUPLICATE_READER" 60 | case SCARD_E_CARD_UNSUPPORTED: 61 | str ="SCARD_E_CARD_UNSUPPORTED" 62 | case SCARD_E_NO_SERVICE: 63 | str ="SCARD_E_NO_SERVICE" 64 | case SCARD_E_SERVICE_STOPPED: 65 | str ="SCARD_E_SERVICE_STOPPED" 66 | case SCARD_E_UNEXPECTED: 67 | str ="SCARD_E_UNEXPECTED" 68 | case SCARD_E_ICC_INSTALLATION: 69 | str ="SCARD_E_ICC_INSTALLATION" 70 | case SCARD_E_ICC_CREATEORDER: 71 | str ="SCARD_E_ICC_CREATEORDER" 72 | case SCARD_E_UNSUPPORTED_FEATURE: 73 | str ="SCARD_E_UNSUPPORTED_FEATURE" 74 | case SCARD_E_DIR_NOT_FOUND: 75 | str ="SCARD_E_DIR_NOT_FOUND" 76 | case SCARD_E_FILE_NOT_FOUND: 77 | str ="SCARD_E_FILE_NOT_FOUND" 78 | case SCARD_E_NO_DIR: 79 | str ="SCARD_E_NO_DIR" 80 | case SCARD_E_NO_FILE: 81 | str ="SCARD_E_NO_FILE" 82 | case SCARD_E_NO_ACCESS: 83 | str ="SCARD_E_NO_ACCESS" 84 | case SCARD_E_WRITE_TOO_MANY: 85 | str ="SCARD_E_WRITE_TOO_MANY" 86 | case SCARD_E_BAD_SEEK: 87 | str ="SCARD_E_BAD_SEEK" 88 | case SCARD_E_INVALID_CHV: 89 | str ="SCARD_E_INVALID_CHV" 90 | case SCARD_E_UNKNOWN_RES_MNG: 91 | str ="SCARD_E_UNKNOWN_RES_MNG" 92 | case SCARD_E_NO_SUCH_CERTIFICATE: 93 | str ="SCARD_E_NO_SUCH_CERTIFICATE" 94 | case SCARD_E_CERTIFICATE_UNAVAILABLE: 95 | str ="SCARD_E_CERTIFICATE_UNAVAILABLE" 96 | case SCARD_E_NO_READERS_AVAILABLE: 97 | str ="SCARD_E_NO_READERS_AVAILABLE" 98 | case SCARD_E_COMM_DATA_LOST: 99 | str ="SCARD_E_COMM_DATA_LOST" 100 | case SCARD_E_NO_KEY_CONTAINER: 101 | str ="SCARD_E_NO_KEY_CONTAINER" 102 | case SCARD_E_SERVER_TOO_BUSY: 103 | str ="SCARD_E_SERVER_TOO_BUSY" 104 | case SCARD_W_UNSUPPORTED_CARD: 105 | str ="SCARD_W_UNSUPPORTED_CARD" 106 | case SCARD_W_UNRESPONSIVE_CARD: 107 | str ="SCARD_W_UNRESPONSIVE_CARD" 108 | case SCARD_W_UNPOWERED_CARD: 109 | str ="SCARD_W_UNPOWERED_CARD" 110 | case SCARD_W_RESET_CARD: 111 | str ="SCARD_W_RESET_CARD" 112 | case SCARD_W_REMOVED_CARD: 113 | str ="SCARD_W_REMOVED_CARD" 114 | case SCARD_W_SECURITY_VIOLATION: 115 | str ="SCARD_W_SECURITY_VIOLATION" 116 | case SCARD_W_WRONG_CHV: 117 | str ="SCARD_W_WRONG_CHV" 118 | case SCARD_W_CHV_BLOCKED: 119 | str ="SCARD_W_CHV_BLOCKED" 120 | case SCARD_W_EOF: 121 | str ="SCARD_W_EOF" 122 | case SCARD_W_CANCELLED_BY_USER: 123 | str ="SCARD_W_CANCELLED_BY_USER" 124 | case SCARD_W_CARD_NOT_AUTHENTICATED : 125 | str ="SCARD_W_CARD_NOT_AUTHENTICATED " 126 | } 127 | return str 128 | } 129 | -------------------------------------------------------------------------------- /smartcard/pcsc/pcsclite_client.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package pcsc 4 | 5 | import ( 6 | "net" 7 | "unsafe" 8 | "bytes" 9 | "fmt" 10 | ) 11 | 12 | const ( 13 | // Protocol version 14 | _PROTOCOL_VERSION_MAJOR = 4 15 | _PROTOCOL_VERSION_MINOR = 3 16 | // Commands 17 | _SCARD_ESTABLISH_CONTEXT = 0x01 18 | _SCARD_RELEASE_CONTEXT = 0x02 19 | _SCARD_LIST_READERS = 0x03 20 | _SCARD_CONNECT = 0x04 21 | _SCARD_RECONNECT = 0x05 22 | _SCARD_DISCONNECT = 0x06 23 | _SCARD_BEGIN_TRANSACTION = 0x07 24 | _SCARD_END_TRANSACTION = 0x08 25 | _SCARD_TRANSMIT = 0x09 26 | _SCARD_CONTROL = 0x0A 27 | _SCARD_STATUS = 0x0B 28 | _SCARD_GET_STATUS_CHANGE = 0x0C 29 | _SCARD_CANCEL = 0x0D 30 | _SCARD_CANCEL_TRANSACTION = 0x0E 31 | _SCARD_GET_ATTRIB = 0x0F 32 | _SCARD_SET_ATTRIB = 0x10 33 | _CMD_VERSION = 0x11 34 | _CMD_GET_READERS_STATE = 0x12 35 | /* 36 | _CMD_WAIT_READER_STATE_CHANGE = 0x13 37 | _CMD_STOP_WAITING_READER_STATE_CHANGE = 0x14 38 | */ 39 | // Limits 40 | _PCSCLITE_MAX_READERS_CONTEXTS = 16 41 | _MAX_READERNAME = 128 42 | ) 43 | 44 | type rxHeader struct { 45 | size uint32 46 | command uint32 47 | } 48 | 49 | type versionStruct struct { 50 | major int32 51 | minor int32 52 | rv uint32 53 | } 54 | 55 | type establishStruct struct { 56 | scope uint32 57 | context uint32 58 | rv uint32 59 | } 60 | 61 | type releaseStruct struct { 62 | context uint32 63 | rv uint32 64 | } 65 | 66 | type connectStruct struct { 67 | context uint32 68 | readerName [_MAX_READERNAME]byte 69 | shareMode uint32 70 | preferredProtocols uint32 71 | card int32 72 | activeProtocol uint32 73 | rv uint32 74 | } 75 | 76 | type disconnectStruct struct { 77 | card int32 78 | disposition uint32 79 | rv uint32 80 | } 81 | 82 | type transmitStruct struct { 83 | card int32 84 | sendPciProtocol uint32 85 | sendPciLength uint32 86 | sendLength uint32 87 | recvPciProtocol uint32 88 | recvPciLength uint32 89 | recvLength uint32 90 | rv uint32 91 | } 92 | 93 | type waitReaderStateChangeStruct struct { 94 | timeOutMs uint32 95 | rv uint32 96 | } 97 | 98 | type Reader struct { 99 | ReaderName [_MAX_READERNAME]byte 100 | EventCounter uint32 101 | ReaderState uint32 102 | ReaderSharing int32 103 | CardAtr [_MAX_ATR_SIZE] byte 104 | CardAtrLength uint32 105 | CardProtocol uint32 106 | } 107 | 108 | func (ri *Reader) Name() string { 109 | n := bytes.IndexByte(ri.ReaderName[:], 0) 110 | return string(ri.ReaderName[:n]) 111 | } 112 | 113 | func (ri *Reader) IsCardPresent() bool { 114 | present := uint32(SCARD_POWERED | SCARD_PRESENT) 115 | return (ri.ReaderState & present) == present 116 | } 117 | 118 | func (ri *Reader) String() string { 119 | var buffer bytes.Buffer 120 | buffer.WriteString(ri.Name()) 121 | buffer.WriteString("\n") 122 | buffer.WriteString(fmt.Sprintf("- Event Counter: %d\n", ri.EventCounter)) 123 | buffer.WriteString(fmt.Sprintf("- Reader State: %x (", ri.ReaderState)) 124 | if (ri.ReaderState & SCARD_SPECIFIC) != 0 { 125 | buffer.WriteString(" SPECFIC") 126 | } 127 | if (ri.ReaderState & SCARD_NEGOTIABLE) != 0 { 128 | buffer.WriteString(" NEGOTIABLE") 129 | } 130 | if (ri.ReaderState & SCARD_POWERED) != 0 { 131 | buffer.WriteString(" POWERED") 132 | } 133 | if (ri.ReaderState & SCARD_SWALLOWED) != 0 { 134 | buffer.WriteString(" SWALLOWED") 135 | } 136 | if (ri.ReaderState & SCARD_PRESENT) != 0 { 137 | buffer.WriteString(" PRESENT") 138 | } 139 | if (ri.ReaderState & SCARD_ABSENT) != 0 { 140 | buffer.WriteString(" ABSENT") 141 | } 142 | if (ri.ReaderState & SCARD_UNKNOWN) != 0 { 143 | buffer.WriteString(" UNKOWN") 144 | } 145 | buffer.WriteString(" )\n") 146 | buffer.WriteString(fmt.Sprintf("- Reader Sharing: %d\n", ri.ReaderSharing)) 147 | buffer.WriteString(fmt.Sprintf("- Card ATR Len: %d\n", ri.CardAtrLength)) 148 | buffer.WriteString("- Card ATR: ") 149 | for y := uint32(0); y < ri.CardAtrLength; y++ { 150 | buffer.WriteString(fmt.Sprintf("%02x", ri.CardAtr[y])) 151 | } 152 | buffer.WriteString(fmt.Sprintf("\n- Card Protocol: %08x\n", 153 | ri.CardProtocol)) 154 | return buffer.String() 155 | } 156 | 157 | type ReaderArray [_PCSCLITE_MAX_READERS_CONTEXTS]Reader 158 | 159 | type PCSCLiteClient struct { 160 | connection net.Conn 161 | readers ReaderArray 162 | readerCount uint32 163 | } 164 | 165 | func PCSCLiteConnect() (*PCSCLiteClient, error) { 166 | var err error 167 | client := &PCSCLiteClient{} 168 | client.connection, err = net.Dial("unix","/var/run/pcscd/pcscd.comm") 169 | if err != nil { return nil, fmt.Errorf("can't connect to PCSCD") } 170 | /* 171 | version := versionStruct{ 172 | _PROTOCOL_VERSION_MAJOR, _PROTOCOL_VERSION_MINOR, 0, 173 | } 174 | ptr1 := (*[unsafe.Sizeof(version)]byte)(unsafe.Pointer(&version)) 175 | err = client.ExchangeMessage(_CMD_VERSION, ptr1[:]) 176 | if err != nil { return nil, err } 177 | if version.rv != SCARD_S_SUCCESS { 178 | return nil, fmt.Errorf("protocol version mismatch") 179 | } 180 | */ 181 | return client, nil 182 | } 183 | 184 | func (client* PCSCLiteClient) Readers() ReaderArray { 185 | return client.readers 186 | } 187 | 188 | func (client *PCSCLiteClient) Close() { 189 | client.connection.Close() 190 | } 191 | 192 | func (client *PCSCLiteClient) SendHeader(command uint32, msgLen uint32) error { 193 | header := rxHeader{size: msgLen, command: command} 194 | headerPtr := (*[unsafe.Sizeof(header)]byte)(unsafe.Pointer(&header)) 195 | _, err := client.connection.Write(headerPtr[:]) 196 | return err 197 | } 198 | 199 | func (client *PCSCLiteClient) ExchangeMessage(cmd uint32, msg []byte) error { 200 | err := client.SendHeader(cmd, uint32(len(msg))) 201 | if err != nil { return err } 202 | _, err = client.connection.Write(msg) 203 | if err != nil { return err } 204 | _, err = client.connection.Read(msg) 205 | return err 206 | } 207 | 208 | func (client *PCSCLiteClient) Read(data []byte) (int, error) { 209 | return client.connection.Read(data) 210 | } 211 | 212 | func (client *PCSCLiteClient) Write(data []byte) (int, error) { 213 | return client.connection.Write(data) 214 | } 215 | 216 | func (client *PCSCLiteClient) EstablishContext(scope ...uint32) (uint32, error) { 217 | scp := uint32(CARD_SCOPE_SYSTEM) 218 | if len(scope) > 0 { 219 | scp = scope[0] 220 | } 221 | estruct := establishStruct{scope: scp} 222 | ptr := (*[unsafe.Sizeof(estruct)]byte)(unsafe.Pointer(&estruct)) 223 | err := client.ExchangeMessage(_SCARD_ESTABLISH_CONTEXT, ptr[:]) 224 | if err != nil { return 0, err } 225 | if estruct.rv != SCARD_S_SUCCESS { 226 | return 0, fmt.Errorf( 227 | "can't establish context: %s", errorString(estruct.rv), 228 | ) 229 | } 230 | return estruct.context, nil 231 | } 232 | 233 | func (client *PCSCLiteClient) ReleaseContext(context uint32) error { 234 | rstruct := releaseStruct{context: context} 235 | ptr := (*[unsafe.Sizeof(rstruct)]byte)(unsafe.Pointer(&rstruct)) 236 | err := client.ExchangeMessage(_SCARD_RELEASE_CONTEXT, ptr[:]) 237 | if err != nil { return err } 238 | if rstruct.rv != SCARD_S_SUCCESS { 239 | return fmt.Errorf("can't release context: %s", errorString(rstruct.rv)) 240 | } 241 | return nil 242 | } 243 | 244 | func (client *PCSCLiteClient) SyncReaders() ( 245 | uint32, error) { 246 | var count uint32 247 | ptr := (*[unsafe.Sizeof(client.readers)]byte)( 248 | unsafe.Pointer(&client.readers)) 249 | err := client.SendHeader(_CMD_GET_READERS_STATE, 0) 250 | _, err = client.Read(ptr[:]) 251 | if err != nil { return count, err } 252 | for count = 0; count < _PCSCLITE_MAX_READERS_CONTEXTS; count++ { 253 | ri := client.readers[count] 254 | if ri.ReaderName[0] == 0 { 255 | break 256 | } 257 | } 258 | client.readerCount = count 259 | return count, nil 260 | } 261 | 262 | func (client *PCSCLiteClient) ListReaders() ([]*Reader, error) { 263 | client.SyncReaders() 264 | readers := make([]*Reader, client.readerCount) 265 | for i := uint32(0); i < client.readerCount; i++ { 266 | readers[i] = &client.readers[i] 267 | } 268 | return readers, nil 269 | } 270 | 271 | func (client *PCSCLiteClient) CardConnect(context uint32, readerName string) ( 272 | int32, uint32, error) { 273 | cstruct := connectStruct{context: context} 274 | readerBytes := ([]byte)(readerName) 275 | limit := len(readerBytes) 276 | if limit > _MAX_READERNAME { limit = _MAX_READERNAME } 277 | for i := 0; i < limit; i++ { 278 | cstruct.readerName[i] = readerBytes[i] 279 | } 280 | cstruct.shareMode = SCARD_SHARE_SHARED 281 | cstruct.preferredProtocols = SCARD_PROTOCOL_ANY 282 | ptr := (*[unsafe.Sizeof(cstruct)]byte)(unsafe.Pointer(&cstruct)) 283 | err := client.ExchangeMessage(_SCARD_CONNECT, ptr[:]) 284 | if err != nil { return 0, 0, err } 285 | if cstruct.rv != SCARD_S_SUCCESS { 286 | return 0, 0, fmt.Errorf("cant connect to card: %s", 287 | errorString(cstruct.rv)) 288 | } 289 | return cstruct.card, cstruct.activeProtocol, nil 290 | } 291 | 292 | func (client *PCSCLiteClient) CardDisconnect(card int32) error { 293 | dstruct := disconnectStruct{ 294 | card: card, 295 | disposition: SCARD_RESET_CARD, 296 | } 297 | ptr := (*[unsafe.Sizeof(dstruct)]byte)(unsafe.Pointer(&dstruct)) 298 | err := client.ExchangeMessage(_SCARD_DISCONNECT, ptr[:]) 299 | if err != nil { return err } 300 | if dstruct.rv != SCARD_S_SUCCESS { 301 | return fmt.Errorf("cant disconnect from card: %s", 302 | errorString(dstruct.rv)) 303 | } 304 | return nil 305 | } 306 | 307 | func (client *PCSCLiteClient) Transmit(card int32, protocol uint32, 308 | sendBuffer []byte, recvBuffer []byte) (uint32, error) { 309 | tstruct := transmitStruct{ 310 | card: card, 311 | sendLength: uint32(len(sendBuffer)), 312 | sendPciProtocol: protocol, 313 | sendPciLength: 8, 314 | recvLength: uint32(len(recvBuffer)), 315 | recvPciProtocol: SCARD_PROTOCOL_ANY, 316 | recvPciLength: 8, 317 | } 318 | tsBytes := (*[unsafe.Sizeof(tstruct)]byte)(unsafe.Pointer(&tstruct))[:] 319 | err := client.SendHeader(_SCARD_TRANSMIT, uint32(len(tsBytes))) 320 | if err != nil { return 0, err } 321 | _, err = client.connection.Write(tsBytes) 322 | if err != nil { return 0, err } 323 | _, err = client.connection.Write(sendBuffer) 324 | if err != nil { return 0, err } 325 | _, err = client.connection.Read(tsBytes) 326 | if err != nil { return 0, err } 327 | if tstruct.rv != SCARD_S_SUCCESS { 328 | return 0, fmt.Errorf("transmission failed: %s", errorString(tstruct.rv)) 329 | } 330 | _, err = client.connection.Read(recvBuffer) 331 | if err != nil { return 0, err } 332 | return tstruct.recvLength, nil 333 | } 334 | 335 | /* 336 | func (client *PCSCLiteClient) WaitReaderStateChange() error { 337 | wrstruct := waitReaderStateChangeStruct{ timeOutMs: uint32(60000) } 338 | ptr := (*[unsafe.Sizeof(wrstruct)]byte)(unsafe.Pointer(&wrstruct)) 339 | err := client.ExchangeMessage(_CMD_WAIT_READER_STATE_CHANGE, ptr[:]) 340 | if err != nil { 341 | return err 342 | } 343 | if wrstruct.rv == SCARD_E_TIMEOUT { 344 | err = client.ExchangeMessage( 345 | _CMD_STOP_WAITING_READER_STATE_CHANGE, ptr[:], 346 | ) 347 | if err != nil { 348 | return err 349 | } 350 | } 351 | if wrstruct.rv != SCARD_S_SUCCESS { 352 | return fmt.Errorf("wait failed: %s", errorString(wrstruct.rv)) 353 | } 354 | return nil 355 | } 356 | */ 357 | -------------------------------------------------------------------------------- /smartcard/pcsc/pcsclite_client_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package pcsc 4 | 5 | import ( 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | var CMD_SELECT = []byte{ 11 | 0x00, 0xA4, 0x04, 0x00, 0x08, 12 | 0x90, 0x72, 0x5A, 0x9E, 0x3B, 0x10, 0x70, 0xAA, 13 | } 14 | 15 | var CMD_10 = []byte{ 16 | 0x00, 0x10, 0x00, 0x00, 0x0B, 17 | } 18 | 19 | func printHex(buffer []byte) { 20 | for _, b := range buffer { 21 | fmt.Printf("%02x", b) 22 | } 23 | fmt.Println("") 24 | } 25 | 26 | func TestClient(t *testing.T) { 27 | fmt.Println("\n=================") 28 | fmt.Println("PCSCD Client Test") 29 | fmt.Printf("=================\n\n") 30 | fmt.Println("Connect to daemon") 31 | fmt.Printf("-----------------\n\n") 32 | client, err := PCSCLiteConnect() 33 | if err != nil { t.Error(err); return } 34 | defer client.Close() 35 | fmt.Println("OK") 36 | 37 | fmt.Println("\nEstablish Context") 38 | fmt.Printf("-----------------\n\n") 39 | context, err := client.EstablishContext() 40 | if err != nil { t.Error(err); return } 41 | defer client.ReleaseContext(context) 42 | fmt.Println("OK") 43 | 44 | fmt.Println("\nList Readers") 45 | fmt.Printf("------------\n\n") 46 | var selectedReader *Reader = nil 47 | readers, err := client.ListReaders() 48 | if err != nil { t.Error(err); return } 49 | for _, reader := range readers { 50 | fmt.Println(reader) 51 | if reader.IsCardPresent() && (selectedReader == nil) { 52 | selectedReader = reader 53 | } 54 | } 55 | 56 | if selectedReader == nil { 57 | fmt.Println("No reader with card found") 58 | return 59 | } 60 | 61 | fmt.Println("Connect to card") 62 | fmt.Printf("---------------\n\n") 63 | card, protocol, err := client.CardConnect( context, selectedReader.Name()) 64 | if err != nil { t.Error(err); return } 65 | fmt.Println("OK") 66 | 67 | fmt.Println("\nSelect applet") 68 | fmt.Printf("-------------\n\n") 69 | buffer := make([]byte, 258) 70 | printHex(CMD_SELECT) 71 | received, err := client.Transmit(card, protocol, CMD_SELECT, buffer) 72 | if err != nil { t.Error(err); return } 73 | printHex(buffer[:received]) 74 | 75 | fmt.Println("\nSend CMD 10") 76 | fmt.Printf("-----------\n\n") 77 | printHex(CMD_10) 78 | received, err = client.Transmit(card, protocol, CMD_10, buffer) 79 | if err != nil { t.Error(err); return } 80 | printHex(buffer[:received]) 81 | fmt.Printf("Quoth the Applet, \"%s\"\n", string(buffer[:received-2])) 82 | 83 | fmt.Println("\nDisconnect from card") 84 | fmt.Printf("--------------------\n\n") 85 | err = client.CardDisconnect(card) 86 | if err != nil { t.Error(err); return } 87 | fmt.Println("OK") 88 | } 89 | -------------------------------------------------------------------------------- /smartcard/pcsc/winscard_wrapper.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package pcsc 4 | 5 | import ( 6 | "fmt" 7 | "bytes" 8 | "unsafe" 9 | "syscall" 10 | ) 11 | 12 | type readerState struct { 13 | reader uintptr 14 | userData uintptr 15 | currentState uint32 16 | eventState uint32 17 | atrLen uint32 18 | atr [_MAX_ATR_SIZE]byte 19 | } 20 | 21 | type ReaderState struct { 22 | Reader string 23 | UserData uintptr 24 | CurrentState uint32 25 | EventState uint32 26 | AtrLen uint32 27 | Atr [_MAX_ATR_SIZE]byte 28 | } 29 | 30 | type WinscardWrapper struct { 31 | establishContext *syscall.LazyProc 32 | releaseContext *syscall.LazyProc 33 | listReaders *syscall.LazyProc 34 | cardConnect *syscall.LazyProc 35 | cardDisconnect *syscall.LazyProc 36 | transmit *syscall.LazyProc 37 | getStatusChange *syscall.LazyProc 38 | getAttrib *syscall.LazyProc 39 | t0PCI uintptr 40 | t1PCI uintptr 41 | } 42 | 43 | var theWrapper *WinscardWrapper = nil 44 | 45 | func Winscard() (*WinscardWrapper, error) { 46 | if theWrapper != nil { 47 | return theWrapper, nil 48 | } 49 | dll := syscall.NewLazyDLL("winscard.dll") 50 | winscard := &WinscardWrapper{} 51 | theWrapper = winscard 52 | winscard.establishContext = dll.NewProc("SCardEstablishContext") 53 | err := winscard.establishContext.Find() 54 | if err != nil { return nil, err } 55 | winscard.releaseContext = dll.NewProc("SCardReleaseContext") 56 | winscard.listReaders = dll.NewProc("SCardListReadersA") 57 | winscard.cardConnect = dll.NewProc("SCardConnectA") 58 | winscard.cardDisconnect = dll.NewProc("SCardDisconnect") 59 | winscard.transmit = dll.NewProc("SCardTransmit") 60 | winscard.getStatusChange = dll.NewProc("SCardGetStatusChangeA") 61 | winscard.getAttrib = dll.NewProc("SCardGetAttrib") 62 | t0 := dll.NewProc("g_rgSCardT0Pci") 63 | t1 := dll.NewProc("g_rgSCardT1Pci") 64 | if t0.Find() != nil || t1.Find() != nil { 65 | fmt.Errorf("pci structures not found") 66 | } 67 | winscard.t0PCI = t0.Addr() 68 | winscard.t1PCI = t1.Addr() 69 | return winscard, nil 70 | } 71 | 72 | func (ww *WinscardWrapper) T0PCI() uintptr { 73 | return ww.t0PCI 74 | } 75 | 76 | func (ww *WinscardWrapper) T1PCI() uintptr { 77 | return ww.t1PCI 78 | } 79 | 80 | func (ww *WinscardWrapper) stringToBytes(str string) []byte { 81 | var buffer bytes.Buffer 82 | buffer.WriteString(str) 83 | buffer.WriteByte(0) 84 | return buffer.Bytes() 85 | } 86 | 87 | func (ww *WinscardWrapper) EstablishContext(scope ...uint32) (uintptr, error) { 88 | var ctx uintptr 89 | scp := uint32(CARD_SCOPE_SYSTEM) 90 | if len(scope) > 0 { 91 | scp = scope[0] 92 | } 93 | rv, _, _ := ww.establishContext.Call(uintptr(scp), uintptr(0), 94 | uintptr(0), uintptr(unsafe.Pointer(&ctx))) 95 | if rv != SCARD_S_SUCCESS { 96 | return 0, fmt.Errorf("can't establish context: %s", 97 | errorString(uint32(rv))) 98 | } 99 | return ctx, nil 100 | } 101 | 102 | func (ww *WinscardWrapper) ReleaseContext(ctx uintptr) error { 103 | rv, _, _ := ww.releaseContext.Call(uintptr(ctx)) 104 | if rv != SCARD_S_SUCCESS { 105 | return fmt.Errorf("can't release context: %s", 106 | errorString(uint32(rv))) 107 | } 108 | return nil 109 | } 110 | 111 | func (ww *WinscardWrapper) ListReaders(ctx uintptr) ([]string, error) { 112 | var bufferSize uintptr 113 | readers := make([]string, 0, 3) 114 | rv, _, _ := ww.listReaders.Call(ctx, 0, 0, 115 | uintptr(unsafe.Pointer(&bufferSize))) 116 | if rv != SCARD_S_SUCCESS { 117 | if rv == SCARD_E_NO_READERS_AVAILABLE { 118 | return readers, nil 119 | } 120 | return nil, fmt.Errorf("can't list readers: %s", 121 | errorString(uint32(rv))) 122 | } 123 | buffer := make([]byte, bufferSize) 124 | rv, _, _ = ww.listReaders.Call(ctx, 0, 125 | uintptr(unsafe.Pointer(&buffer[0])), 126 | uintptr(unsafe.Pointer(&bufferSize))) 127 | if rv != SCARD_S_SUCCESS { 128 | return nil, fmt.Errorf("can't list readers: %s", 129 | errorString(uint32(rv))) 130 | } 131 | n := bytes.IndexByte(buffer, 0) 132 | for n != 0 { 133 | readers = append(readers, string(buffer[:n])) 134 | buffer = buffer[n+1:] 135 | n = bytes.IndexByte(buffer, 0) 136 | } 137 | return readers, nil 138 | } 139 | 140 | func (ww *WinscardWrapper) GetStatusChange(ctx uintptr, timeout uint32, 141 | states []ReaderState) error { 142 | _states := make([]readerState, len(states)) 143 | for i := 0; i < len(states); i++ { 144 | var buffer bytes.Buffer 145 | buffer.WriteString(states[i].Reader) 146 | buffer.WriteByte(0) 147 | _states[i].reader = uintptr(unsafe.Pointer(&buffer.Bytes()[0])) 148 | _states[i].currentState = states[i].CurrentState 149 | } 150 | rv, _, _ := ww.getStatusChange.Call(ctx, uintptr(timeout), 151 | uintptr(unsafe.Pointer(&_states[0])), uintptr(len(_states))) 152 | if rv != SCARD_S_SUCCESS { 153 | return fmt.Errorf("get status change failed: %s", 154 | errorString(uint32(rv))) 155 | } 156 | for i := 0; i < len(states); i++ { 157 | states[i].UserData = _states[i].userData 158 | states[i].EventState = _states[i].eventState 159 | states[i].AtrLen = _states[i].atrLen 160 | states[i].Atr = _states[i].atr 161 | } 162 | return nil 163 | } 164 | 165 | func (ww *WinscardWrapper) GetStatusChangeAll(ctx uintptr, timeout uint32, 166 | currentState uint32) ([]ReaderState, error) { 167 | readerNames, err := ww.ListReaders(ctx) 168 | if err != nil { return nil, err } 169 | count := len(readerNames) 170 | if count == 0 { 171 | return nil, nil 172 | } 173 | states := make([]ReaderState, len(readerNames)) 174 | for i := 0; i < len(readerNames); i++ { 175 | states[i].Reader = readerNames[i] 176 | states[i].CurrentState = currentState 177 | } 178 | err = ww.GetStatusChange(ctx, SCARD_INFINITE, states) 179 | if err != nil { return nil, err } 180 | return states, nil 181 | } 182 | 183 | func (ww *WinscardWrapper) CardConnect(ctx uintptr, reader string) ( 184 | uintptr, uintptr, error) { 185 | var card, activeProtocol uintptr 186 | rv, _, _ := ww.cardConnect.Call( 187 | ctx, 188 | uintptr(unsafe.Pointer(unsafe.Pointer(&ww.stringToBytes(reader)[0]))), 189 | uintptr(SCARD_SHARE_SHARED), uintptr(SCARD_PROTOCOL_ANY), 190 | uintptr(unsafe.Pointer(&card)), 191 | uintptr(unsafe.Pointer(&activeProtocol)), 192 | ) 193 | if rv != SCARD_S_SUCCESS { 194 | return 0, 0, fmt.Errorf("can't connect to card: %s", 195 | errorString(uint32(rv))) 196 | } 197 | return card, activeProtocol, nil 198 | } 199 | 200 | func (ww *WinscardWrapper) CardDisconnect(card uintptr) error { 201 | rv, _, _ := ww.cardDisconnect.Call(card, uintptr(SCARD_RESET_CARD)) 202 | if rv != SCARD_S_SUCCESS { 203 | return fmt.Errorf("can't disconnect from card: %s", 204 | errorString(uint32(rv))) 205 | } 206 | return nil 207 | } 208 | 209 | func (ww *WinscardWrapper) Transmit(card uintptr, sendPCI uintptr, 210 | sendBuffer []byte, recvBuffer []byte) (uint32, error) { 211 | received := uint32(len(recvBuffer)) 212 | rv, _, _ := ww.transmit.Call(card, 213 | sendPCI, 214 | uintptr(unsafe.Pointer(&sendBuffer[0])), 215 | uintptr(len(sendBuffer)), 216 | 0, 217 | uintptr(unsafe.Pointer(&recvBuffer[0])), 218 | uintptr(unsafe.Pointer(&received))) 219 | if rv != SCARD_S_SUCCESS { 220 | return 0, fmt.Errorf("transmission failed: %s", 221 | errorString(uint32(rv))) 222 | } 223 | return received, nil 224 | } 225 | 226 | func (ww WinscardWrapper) GetAttrib(card uintptr, attr uint32) ([]byte, error) { 227 | var size uintptr 228 | rv, _, _ := ww.getAttrib.Call( 229 | card, uintptr(attr), 0, 230 | uintptr(unsafe.Pointer(&size)), 231 | ) 232 | if rv != SCARD_S_SUCCESS { 233 | return nil, fmt.Errorf("can't get attribute : %s", 234 | errorString(uint32(rv))) 235 | } 236 | buffer := make([]byte, size) 237 | rv, _, _ = ww.getAttrib.Call( 238 | card, uintptr(attr), 239 | uintptr(unsafe.Pointer(&buffer[0])), 240 | uintptr(unsafe.Pointer(&size)), 241 | ) 242 | if rv != SCARD_S_SUCCESS { 243 | return nil, fmt.Errorf("can't get attribute : %s", 244 | errorString(uint32(rv))) 245 | } 246 | return buffer[:size], nil 247 | } 248 | -------------------------------------------------------------------------------- /smartcard/smartcard.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package smartcard implements a portable high-level API for communicating with smart cards. 3 | 4 | Example: 5 | 6 | ctx, err := smartcard.EstablishContext() 7 | // handle error, if any 8 | defer ctx.Release() 9 | 10 | reader, err := ctx.WaitForCardPresent() 11 | // handle error, if any 12 | 13 | card, err := reader.Connect() 14 | // handle error, if any 15 | defer card.Disconnect() 16 | 17 | fmt.Printf("Card ATR: %s\n", card.ATR()) 18 | command := SelectCommand(0xa0, 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0xc, 0x01, 0x01) 19 | response, err := card.TransmitAPDU(command) 20 | // handle error, if any 21 | fmt.Printf("Response: %s\n", response) 22 | */ 23 | package smartcard 24 | 25 | import ( 26 | "fmt" 27 | "bytes" 28 | "github.com/sf1/go-card/smartcard/pcsc" 29 | ) 30 | 31 | const ( 32 | // Scope 33 | SCOPE_USER = pcsc.CARD_SCOPE_USER 34 | SCOPE_TERMINAL = pcsc.CARD_SCOPE_TERMINAL 35 | SCOPE_SYSTEM = pcsc.CARD_SCOPE_SYSTEM 36 | ) 37 | 38 | type ATR []byte 39 | 40 | // Return string form of ATR. 41 | func (atr ATR) String() string { 42 | var buffer bytes.Buffer 43 | for _, b := range atr { 44 | buffer.WriteString(fmt.Sprintf("%02x", b)) 45 | } 46 | return buffer.String() 47 | } 48 | 49 | // Transmit command APDU to the card and return response. 50 | func (c *Card) TransmitAPDU(cmd CommandAPDU) (ResponseAPDU, error) { 51 | bytes, err := c.Transmit(cmd) 52 | if err != nil { return nil, err } 53 | r, err := Response(bytes) 54 | if err != nil { return nil, err } 55 | return r, nil 56 | } 57 | 58 | // ISO7816-4 command APDU. 59 | type CommandAPDU []byte 60 | 61 | // Create command APDU with CLA, INS, P1, P2 as specified. 62 | // No command data, no response required. 63 | func Command1(cla, ins, p1, p2 byte) CommandAPDU { 64 | var cmd CommandAPDU = make([]byte, 4) 65 | cmd[0] = cla 66 | cmd[1] = ins 67 | cmd[2] = p1 68 | cmd[3] = p2 69 | return cmd 70 | } 71 | 72 | // Create command APDU with CLA, INS, P1, P2 as specified. 73 | // Response of length Le required. 74 | func Command2(cla, ins, p1, p2, le byte) CommandAPDU { 75 | var cmd CommandAPDU = make([]byte, 5) 76 | cmd[0] = cla 77 | cmd[1] = ins 78 | cmd[2] = p1 79 | cmd[3] = p2 80 | cmd[4] = le 81 | return cmd 82 | } 83 | 84 | // Create command APDU with CLA, INS, P1, P2 and data as specified. 85 | // No response required. 86 | func Command3(cla, ins, p1, p2 byte, data []byte) CommandAPDU { 87 | var cmd CommandAPDU = make([]byte, 5 + len(data)) 88 | cmd[0] = cla 89 | cmd[1] = ins 90 | cmd[2] = p1 91 | cmd[3] = p2 92 | cmd[4] = byte(len(data)) 93 | for idx, b := range data { 94 | cmd[5+idx] = b 95 | } 96 | return cmd 97 | } 98 | 99 | // Create command APDU with CLA, INS, P1, P2 and data as specified. 100 | // Response of length Le required. 101 | func Command4(cla, ins, p1, p2 byte, data []byte, le byte) CommandAPDU { 102 | cmd := Command3(cla, ins, p1, p2, data) 103 | cmd = append(cmd, le) 104 | return cmd 105 | } 106 | 107 | // Create ISO7816-4 SELECT FILE APDU. 108 | func SelectCommand(aid ...byte) CommandAPDU { 109 | return Command3(0x00, 0xa4, 0x04, 0x00, aid) 110 | } 111 | 112 | // Check if command APDU is valid 113 | func (cmd CommandAPDU) IsValid() bool { 114 | cmdLen := byte(len(cmd)) 115 | if cmdLen < 4 { 116 | return false 117 | } 118 | if cmdLen == 4 || cmdLen == 5 { 119 | return true 120 | } 121 | lc := cmd[4] 122 | if lc > cmdLen - 5 { 123 | return false 124 | } 125 | if cmdLen - 5 > lc + 1 { 126 | return false 127 | } 128 | return true 129 | } 130 | 131 | // Return string form of APDU. 132 | func (cmd CommandAPDU) String() string { 133 | if !cmd.IsValid() { 134 | return "Invalid APDU" 135 | } 136 | apdu := ([]byte)(cmd) 137 | buffer := new(bytes.Buffer) 138 | buffer.WriteString(fmt.Sprintf("%02X %02X %02X %02X", apdu[0], apdu[1], 139 | apdu[2], apdu[3])) 140 | if len(apdu) >= 5 { 141 | buffer.WriteString(fmt.Sprintf(" %02X", apdu[4])) 142 | if len(apdu) >= 6 { 143 | if len(apdu) == int(apdu[4] + 5) { 144 | buffer.WriteString(fmt.Sprintf(" %X", apdu[5:])) 145 | } else { 146 | buffer.WriteString(fmt.Sprintf(" %X %02X", apdu[5:len(apdu)-1], 147 | apdu[len(apdu)-1])) 148 | } 149 | } 150 | } 151 | return buffer.String() 152 | } 153 | 154 | // ISO7816-4 response APDU. 155 | type ResponseAPDU []byte 156 | 157 | func Response(bytes []byte) (ResponseAPDU, error) { 158 | if len(bytes) < 2 { 159 | return nil, fmt.Errorf("Invalid response apdu size: %d", len(bytes)) 160 | } 161 | return ResponseAPDU(bytes), nil 162 | } 163 | 164 | // Return 16-bit status word. 165 | func (r ResponseAPDU) SW() uint16 { 166 | return uint16(r[len(r)-2]) << 8 | uint16(r[len(r)-1]) 167 | } 168 | 169 | // Return SW1 170 | func (r ResponseAPDU) SW1() uint8 { 171 | return r[len(r)-2] 172 | } 173 | 174 | // Return SW2 175 | func (r ResponseAPDU) SW2() uint8 { 176 | return r[len(r)-1] 177 | } 178 | 179 | // Return data part of response 180 | func (r ResponseAPDU) Data() []byte { 181 | if len(r) <= 2 { 182 | return nil 183 | } 184 | return r[:len(r)-2] 185 | } 186 | 187 | // Return string form of APDU. 188 | func (r ResponseAPDU) String() string { 189 | var bytes []byte = r 190 | if len(r) <= 2 { 191 | return fmt.Sprintf("%X", bytes) 192 | } 193 | return fmt.Sprintf("%X %X", bytes[:len(bytes)-2], bytes[len(bytes)-2:]) 194 | } 195 | 196 | -------------------------------------------------------------------------------- /smartcard/smartcard_pcsclite.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package smartcard 4 | 5 | import ( 6 | "time" 7 | "github.com/sf1/go-card/smartcard/pcsc" 8 | ) 9 | 10 | // A smart card context is required to access readers and cards. 11 | type Context struct { 12 | client *pcsc.PCSCLiteClient 13 | ctxID uint32 14 | } 15 | 16 | // Establish smart card context. 17 | // This should be the first function to be called. 18 | func EstablishContext(scope ...uint32) (*Context, error) { 19 | var err error 20 | scp := uint32(SCOPE_SYSTEM) 21 | if len(scope) > 0 { 22 | scp = scope[0] 23 | } 24 | context := &Context{} 25 | context.client, err = pcsc.PCSCLiteConnect() 26 | if err != nil { return nil, err } 27 | context.ctxID, err = context.client.EstablishContext(scp) 28 | return context, nil 29 | } 30 | 31 | // Release resources associated with smart card context. 32 | func (ctx *Context) Release() error { 33 | return ctx.client.ReleaseContext(ctx.ctxID) 34 | } 35 | 36 | // List all smart card readers. 37 | func (ctx *Context) ListReaders() ([]*Reader, error) { 38 | return ctx.listReaders(false) 39 | } 40 | 41 | // List smart card readers with inserted cards. 42 | func (ctx *Context) ListReadersWithCard() ([]*Reader, error) { 43 | return ctx.listReaders(true) 44 | } 45 | 46 | func (ctx *Context) listReaders(withCard bool) ([]*Reader, error) { 47 | readers, err := ctx.client.ListReaders() 48 | if err != nil { return nil, err } 49 | result := make([]*Reader, 0, len(readers)) 50 | for i := 0; i < len(readers); i++ { 51 | if withCard { 52 | if readers[i].IsCardPresent() { 53 | result = append(result, &Reader{ 54 | context: ctx, reader: *readers[i]}) 55 | } 56 | } else { 57 | result = append(result, &Reader{ 58 | context: ctx, reader: *readers[i]}) 59 | } 60 | } 61 | return result, nil 62 | } 63 | 64 | // Block until a smart card is inserted into any reader. 65 | // Returns immediately if card already present. 66 | func (ctx *Context) WaitForCardPresent() (*Reader, error) { 67 | var reader *Reader 68 | for reader == nil { 69 | count, err := ctx.client.SyncReaders() 70 | if err != nil { return nil, err} 71 | for i := uint32(0); i < count; i++ { 72 | r := ctx.client.Readers()[i] 73 | if r.IsCardPresent() { 74 | reader = &Reader{context: ctx, reader: r} 75 | break 76 | } 77 | } 78 | if reader != nil { 79 | break 80 | } 81 | time.Sleep(250*time.Millisecond) 82 | } 83 | return reader, nil 84 | } 85 | 86 | // Smart card reader. 87 | // Note that physical card readers with slots for multiple cards are 88 | // represented by one Reader instance per slot. 89 | type Reader struct { 90 | context *Context 91 | reader pcsc.Reader 92 | } 93 | 94 | // Return name of card reader. 95 | func (r *Reader) Name() string { 96 | return r.reader.Name() 97 | } 98 | 99 | // Check if card is present. 100 | func (r *Reader) IsCardPresent() bool { 101 | count, err := r.context.client.SyncReaders() 102 | if err != nil { 103 | return false 104 | } 105 | readers := r.context.client.Readers() 106 | for i := uint32(0); i < count; i++ { 107 | if r.Name() == readers[i].Name() { 108 | r.reader = readers[i] 109 | if r.reader.IsCardPresent() { 110 | return true 111 | } 112 | } 113 | } 114 | return false 115 | } 116 | 117 | func (r *Reader) WaitUntilCardRemoved() { 118 | for r.IsCardPresent() { 119 | time.Sleep(250*time.Millisecond) 120 | } 121 | } 122 | 123 | // Connect to card. 124 | func (r *Reader) Connect() (*Card, error) { 125 | cardID, protocol, err := r.context.client.CardConnect( 126 | r.context.ctxID, r.reader.Name()) 127 | if err != nil { return nil, err } 128 | return &Card{ 129 | context: r.context, 130 | cardID: cardID, 131 | protocol: protocol, 132 | atr: r.reader.CardAtr[:r.reader.CardAtrLength], 133 | }, nil 134 | } 135 | 136 | // Smart card. 137 | type Card struct { 138 | context *Context 139 | cardID int32 140 | protocol uint32 141 | atr ATR 142 | } 143 | 144 | // Return card ATR (answer to reset). 145 | func (c *Card) ATR() ATR { 146 | return c.atr 147 | } 148 | 149 | // Trasmit bytes to card and return response. 150 | func (c *Card) Transmit(command []byte) ([]byte, error) { 151 | response := make([]byte, 258) 152 | received, err := c.context.client.Transmit(c.cardID, c.protocol, 153 | command, response) 154 | if err != nil { return nil, err } 155 | return response[:received], nil 156 | } 157 | 158 | // Disconnect from card. 159 | func (c *Card) Disconnect() error { 160 | err := c.context.client.CardDisconnect(c.cardID) 161 | if err != nil { return err } 162 | return nil 163 | } 164 | -------------------------------------------------------------------------------- /smartcard/smartcard_test.go: -------------------------------------------------------------------------------- 1 | package smartcard 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestInfo(t *testing.T) { 9 | fmt.Println("\n===================") 10 | fmt.Println("High Level API Test") 11 | fmt.Printf("===================\n\n") 12 | } 13 | 14 | func TestEstablishReleaseUserContext(t *testing.T) { 15 | fmt.Println("------------------------------") 16 | fmt.Println("Test establish/release User Context") 17 | fmt.Printf("------------------------------\n\n") 18 | ctx, err := EstablishContext(SCOPE_USER) 19 | if err != nil { t.Error(err); return } 20 | err = ctx.Release() 21 | if err != nil { t.Error(err); return } 22 | fmt.Printf("OK\n\n") 23 | } 24 | 25 | func TestEstablishReleaseSystemContext(t *testing.T) { 26 | fmt.Println("------------------------------") 27 | fmt.Println("Test establish/release System Context") 28 | fmt.Printf("------------------------------\n\n") 29 | ctx, err := EstablishContext(SCOPE_SYSTEM) 30 | if err != nil { t.Error(err); return } 31 | err = ctx.Release() 32 | if err != nil { t.Error(err); return } 33 | fmt.Printf("OK\n\n") 34 | } 35 | 36 | func TestListReaders(t *testing.T) { 37 | fmt.Println("-----------------") 38 | fmt.Println("Test list readers") 39 | fmt.Printf("-----------------\n\n") 40 | ctx, err := EstablishContext() 41 | if err != nil { t.Error(err); return } 42 | defer ctx.Release() 43 | readers, err := ctx.ListReaders() 44 | if err != nil { t.Error(err); return } 45 | for _, reader := range readers { 46 | fmt.Println(reader.Name()) 47 | fmt.Printf("- Card present: %t\n\n", reader.IsCardPresent()) 48 | } 49 | } 50 | 51 | func TestListReadersWithCard(t *testing.T) { 52 | fmt.Println("---------------------------") 53 | fmt.Println("Test list readers with card") 54 | fmt.Printf("---------------------------\n\n") 55 | ctx, err := EstablishContext() 56 | if err != nil { t.Error(err); return } 57 | defer ctx.Release() 58 | readers, err := ctx.ListReadersWithCard() 59 | if err != nil { t.Error(err); return } 60 | for _, reader := range readers { 61 | fmt.Println(reader.Name()) 62 | fmt.Printf("- Card present: %t\n\n", reader.IsCardPresent()) 63 | } 64 | } 65 | 66 | func TestWaitForCardPresentRemoved(t *testing.T) { 67 | fmt.Println("----------------------------------------------------") 68 | fmt.Println("Test wait for card present / wait until card removed") 69 | fmt.Printf("----------------------------------------------------\n\n") 70 | ctx, err := EstablishContext() 71 | if err != nil { t.Error(err); return } 72 | defer ctx.Release() 73 | fmt.Printf("Insert card now...") 74 | reader, err := ctx.WaitForCardPresent() 75 | if err != nil { t.Error(err); return } 76 | fmt.Printf("\n\n%s\n", reader.Name()) 77 | fmt.Printf("- Card present: %t\n\n", reader.IsCardPresent()) 78 | fmt.Printf("Remove card now...") 79 | reader.WaitUntilCardRemoved() 80 | fmt.Printf("\n\nCard was removed\n\n") 81 | } 82 | 83 | func TestCardCommunication(t *testing.T) { 84 | fmt.Println("-----------------------") 85 | fmt.Println("Test card communication") 86 | fmt.Printf("-----------------------\n\n") 87 | ctx, err := EstablishContext() 88 | if err != nil { t.Error(err); return } 89 | defer ctx.Release() 90 | readers, err := ctx.ListReadersWithCard() 91 | if err != nil { t.Error(err); return } 92 | if len(readers) == 0 { 93 | t.Error("No reader with card") 94 | return 95 | } 96 | reader := readers[0] 97 | fmt.Println("Connect to card") 98 | fmt.Printf("---------------\n\n") 99 | card, err := reader.Connect() 100 | if err != nil { t.Error(err); return } 101 | fmt.Printf("ATR: %s\n\n", card.ATR()) 102 | 103 | fmt.Println("Select applet") 104 | fmt.Printf("-------------\n\n") 105 | cmd := SelectCommand(0x90, 0x72, 0x5A, 0x9E, 0x3B, 0x10, 0x70, 0xAA) 106 | fmt.Printf(">> %s\n", cmd) 107 | response, err := card.TransmitAPDU(cmd) 108 | if err != nil { t.Error(err); return } 109 | fmt.Printf("<< %s\n", response.String()) 110 | 111 | fmt.Println("\nSend CMD 10") 112 | fmt.Printf("-----------\n\n") 113 | cmd = Command2(0x00, 0x10, 0x00, 0x00, 0x0b) 114 | fmt.Printf(">> %s\n", cmd) 115 | response, err = card.TransmitAPDU(cmd) 116 | if err != nil { t.Error(err); return } 117 | fmt.Printf("<< %s\n", response) 118 | fmt.Printf("\nQuoth the Applet, \"%s\"\n\n", string(response.Data())) 119 | 120 | fmt.Println("Disconnect from card") 121 | fmt.Printf("--------------------\n\n") 122 | err = card.Disconnect() 123 | if err != nil { t.Error(err); return } 124 | fmt.Printf("OK\n\n") 125 | } 126 | -------------------------------------------------------------------------------- /smartcard/smartcard_winscard.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package smartcard 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | "github.com/sf1/go-card/smartcard/pcsc" 9 | ) 10 | 11 | // A smart card context is required to access readers and cards. 12 | type Context struct { 13 | ctxID uintptr 14 | winscard *pcsc.WinscardWrapper 15 | } 16 | 17 | // Establish smart card context. 18 | // This should be the first function to be called. 19 | func EstablishContext(scope ...uint32) (*Context, error) { 20 | scp := uint32(SCOPE_SYSTEM) 21 | if len(scope) > 0 { 22 | scp = scope[0] 23 | } 24 | winscard, err := pcsc.Winscard() 25 | if err != nil { 26 | return nil, err 27 | } 28 | ctxID, err := winscard.EstablishContext(scp) 29 | if err != nil { return nil, err } 30 | return &Context{ctxID, winscard}, nil 31 | } 32 | 33 | // Release resources associated with smart card context. 34 | func (ctx *Context) Release() error { 35 | return ctx.winscard.ReleaseContext(ctx.ctxID) 36 | } 37 | 38 | // List all smart card readers. 39 | func (ctx *Context) ListReaders() ([]*Reader, error) { 40 | readerNames, err := ctx.winscard.ListReaders(ctx.ctxID) 41 | if err != nil { return nil, err } 42 | readers := make([]*Reader, len(readerNames)) 43 | for i := 0; i < len(readerNames); i++ { 44 | readers[i] = &Reader{context: ctx, name: readerNames[i]} 45 | } 46 | return readers, nil 47 | } 48 | 49 | // List smart card readers with inserted cards. 50 | func (ctx *Context) ListReadersWithCard() ([]*Reader, error) { 51 | states, err := ctx.winscard.GetStatusChangeAll( 52 | ctx.ctxID, pcsc.SCARD_INFINITE, pcsc.SCARD_STATE_UNAWARE) 53 | if err != nil { return nil, err } 54 | readers := make([]*Reader, 0, len(states)) 55 | for _, state := range states { 56 | if state.EventState & pcsc.SCARD_STATE_MUTE != 0 { 57 | continue 58 | } 59 | if state.EventState & pcsc.SCARD_STATE_PRESENT != 0 { 60 | readers = append(readers, &Reader{context: ctx, name: state.Reader}) 61 | } 62 | } 63 | return readers, nil 64 | } 65 | 66 | // Block until a smart card is inserted into any reader. 67 | // Returns immediately if card already present. 68 | func (ctx *Context) WaitForCardPresent() (*Reader, error) { 69 | var err error 70 | var reader *Reader = nil 71 | var states []pcsc.ReaderState 72 | for reader == nil { 73 | states, err = ctx.winscard.GetStatusChangeAll( 74 | ctx.ctxID, pcsc.SCARD_INFINITE, pcsc.SCARD_STATE_UNAWARE) 75 | if err != nil { return nil, err } 76 | if states == nil { 77 | time.Sleep(500*time.Millisecond) 78 | continue 79 | } 80 | for _, state := range states { 81 | state.CurrentState = state.EventState 82 | if state.EventState & pcsc.SCARD_STATE_MUTE != 0 { 83 | continue 84 | } 85 | if state.EventState & pcsc.SCARD_STATE_PRESENT != 0 { 86 | reader = &Reader{context: ctx, name: state.Reader} 87 | break 88 | } 89 | } 90 | if reader == nil { 91 | err = ctx.winscard.GetStatusChange( 92 | ctx.ctxID, pcsc.SCARD_INFINITE, states, 93 | ) 94 | if err != nil { return nil, err } 95 | time.Sleep(500*time.Millisecond) 96 | } 97 | } 98 | return reader, nil 99 | } 100 | 101 | // Smart card reader. 102 | // Note that physical card readers with slots for multiple cards are 103 | // represented by one Reader instance per slot. 104 | type Reader struct { 105 | context *Context 106 | name string 107 | } 108 | 109 | // Return name of card reader. 110 | func (r *Reader) Name() string { 111 | return r.name 112 | } 113 | 114 | // Check if card is present. 115 | func (r *Reader) IsCardPresent() bool { 116 | states := make([]pcsc.ReaderState, 1) 117 | states[0].Reader = r.name 118 | states[0].CurrentState = pcsc.SCARD_STATE_UNAWARE 119 | err := r.context.winscard.GetStatusChange(r.context.ctxID, 120 | pcsc.SCARD_INFINITE, states) 121 | if err != nil { 122 | return false 123 | } 124 | if states[0].EventState & pcsc.SCARD_STATE_MUTE != 0 { 125 | return false 126 | } 127 | return states[0].EventState & pcsc.SCARD_STATE_PRESENT != 0 128 | } 129 | 130 | // Wait until card removed 131 | func (r *Reader) WaitUntilCardRemoved() { 132 | states := make([]pcsc.ReaderState, 1) 133 | for { 134 | states[0].Reader = r.name 135 | states[0].CurrentState = pcsc.SCARD_STATE_UNAWARE 136 | err := r.context.winscard.GetStatusChange(r.context.ctxID, 137 | pcsc.SCARD_INFINITE, states) 138 | if err != nil { 139 | return 140 | } 141 | if states[0].EventState & pcsc.SCARD_STATE_MUTE != 0 || 142 | states[0].EventState & pcsc.SCARD_STATE_PRESENT == 0 { 143 | return 144 | } 145 | time.Sleep(500*time.Millisecond) 146 | } 147 | } 148 | 149 | 150 | // Connect to card. 151 | func (r *Reader) Connect() (*Card, error) { 152 | var pci uintptr 153 | cardID, protocol, err := r.context.winscard.CardConnect( 154 | r.context.ctxID, r.name) 155 | if err != nil { return nil, err } 156 | switch(protocol) { 157 | case pcsc.SCARD_PROTOCOL_T0: 158 | pci = r.context.winscard.T0PCI() 159 | case pcsc.SCARD_PROTOCOL_T1: 160 | pci = r.context.winscard.T1PCI() 161 | default: 162 | return nil, fmt.Errorf("Unknown protocol: %08x", protocol) 163 | } 164 | return &Card{ 165 | context:r.context, 166 | cardID: cardID, 167 | sendPCI: pci, 168 | }, nil 169 | } 170 | 171 | // Smart card. 172 | type Card struct { 173 | context *Context 174 | cardID uintptr 175 | sendPCI uintptr 176 | atr ATR 177 | } 178 | 179 | // Disconnect from card. 180 | func (c *Card) Disconnect() error { 181 | err := c.context.winscard.CardDisconnect(c.cardID) 182 | if err != nil { return err } 183 | return nil 184 | } 185 | 186 | // Return card ATR (answer to reset). 187 | func (c *Card) ATR() ATR { 188 | var err error 189 | if c.atr != nil { return c.atr } 190 | c.atr, err = c.context.winscard.GetAttrib(c.cardID, pcsc.SCARD_ATTR_ATR_STRING) 191 | if err != nil { return nil } 192 | return c.atr 193 | } 194 | 195 | // Trasmit bytes to card and return response. 196 | func (c *Card) Transmit(command []byte) ([]byte, error) { 197 | response := make([]byte, 258) 198 | received, err := c.context.winscard.Transmit(c.cardID, c.sendPCI, 199 | command, response) 200 | if err != nil { return nil, err } 201 | return response[:received], nil 202 | } 203 | --------------------------------------------------------------------------------