Skip to content

Instantly share code, notes, and snippets.

@bouroo
Last active July 8, 2025 08:51
Show Gist options
  • Select an option

  • Save bouroo/a3aa857727d909283c33e7b00623b48d to your computer and use it in GitHub Desktop.

Select an option

Save bouroo/a3aa857727d909283c33e7b00623b48d to your computer and use it in GitHub Desktop.

Revisions

  1. bouroo revised this gist Jun 1, 2025. 1 changed file with 34 additions and 33 deletions.
    67 changes: 34 additions & 33 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -26,15 +26,15 @@ type SmartCardService interface {
    ATR() ([]byte, error)
    AdjustRequest([]byte)
    Transmit([]byte) ([]byte, error)
    GetData([]byte) ([]byte, error)
    GetData(cmd []byte) ([]byte, error)
    }

    type DataDecoder interface {
    Decode([]byte) ([]byte, error)
    }

    type FileWriter interface {
    Write(string, []byte) error
    Write(path string, data []byte) error
    }

    // Implementations
    @@ -72,37 +72,37 @@ func (c *ScardConnector) Connect(ctx *scard.Context, reader string) (*scard.Card
    return ctx.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    }

    // scardService implements SmartCardService over scard.Card.
    type scardService struct {
    // SmartCardService implementation for scard.Card
    type smartCardService struct {
    card *scard.Card
    reqPrefix []byte
    }

    func NewScardService(card *scard.Card, req []byte) SmartCardService {
    func NewSmartCardService(card *scard.Card, req []byte) SmartCardService {
    copyReq := make([]byte, len(req))
    copy(copyReq, req)
    return &scardService{card: card, reqPrefix: copyReq}
    return &smartCardService{card: card, reqPrefix: copyReq}
    }

    func (s *scardService) Status() (scard.Status, error) {
    func (s *smartCardService) Status() (scard.Status, error) {
    return s.card.Status()
    }

    func (s *scardService) ATR() ([]byte, error) {
    func (s *smartCardService) ATR() ([]byte, error) {
    return s.card.GetAttrib(scard.AttrAtrString)
    }

    func (s *scardService) AdjustRequest(atr []byte) {
    func (s *smartCardService) AdjustRequest(atr []byte) {
    if len(atr) > 1 && atr[0] == 0x3B && atr[1] == 0x67 {
    s.reqPrefix[3] = 0x01
    }
    }

    func (s *scardService) Transmit(cmd []byte) ([]byte, error) {
    func (s *smartCardService) Transmit(cmd []byte) ([]byte, error) {
    return s.card.Transmit(cmd)
    }

    func (s *scardService) GetData(cmd []byte) ([]byte, error) {
    func (s *smartCardService) GetData(cmd []byte) ([]byte, error) {
    if _, err := s.card.Transmit(cmd); err != nil {
    return nil, err
    }
    @@ -123,9 +123,11 @@ func (w *OSFileWriter) Write(path string, data []byte) error {

    // CommandProvider holds all APDUs.
    type CommandProvider struct {
    SelectThai []byte
    Infos []InfoCommand
    Photos [][]byte
    SelectThai []byte
    Infos []InfoCommand
    Photos [][]byte
    reqPrefix []byte
    reqPrefixLen int
    }

    type InfoCommand struct {
    @@ -154,6 +156,8 @@ func NewCommandProvider() *CommandProvider {
    }
    return out
    }(),
    reqPrefix: []byte{0x00, 0xC0, 0x00, 0x00},
    reqPrefixLen: len([]byte{0x00, 0xC0, 0x00, 0x00}),
    }
    }

    @@ -191,11 +195,11 @@ func (p *CardProcessor) Process() error {
    if err != nil {
    return err
    }
    dec, err := p.decoder.Decode(raw)
    decoded, err := p.decoder.Decode(raw)
    if err != nil {
    return err
    }
    fmt.Printf("%s: %s\n", ic.Name, bytes.TrimSpace(dec))
    fmt.Printf("%s: %s\n", ic.Name, bytes.TrimSpace(decoded))
    }

    // photo
    @@ -207,47 +211,44 @@ func (p *CardProcessor) Process() error {
    }
    img = append(img, chunk...)
    }
    return p.writer.Write(p.cmds.Infos[0].Name+".jpg", img)
    return p.writer.Write("output.jpg", img)
    }

    func main() {
    ctx, err := scard.EstablishContext()
    if err != nil {
    fmt.Println(err)
    fmt.Println("Failed to establish context:", err)
    return
    }
    defer ctx.Release()

    readers, err := ctx.ListReaders()
    if err != nil || len(readers) == 0 {
    fmt.Println("no readers")
    if err != nil {
    fmt.Println("Failed to list readers:", err)
    return
    }

    sel := &ConsoleReaderSelector{}
    idx, err := sel.Select(readers)
    idx, err := (&ConsoleReaderSelector{}).Select(readers)
    if err != nil {
    fmt.Println(err)
    fmt.Println("Failed to select reader:", err)
    return
    }

    conn := &ScardConnector{}
    card, err := conn.Connect(ctx, readers[idx])
    connector := &ScardConnector{}
    card, err := connector.Connect(ctx, readers[idx])
    if err != nil {
    fmt.Println(err)
    fmt.Println("Failed to connect to card:", err)
    return
    }
    defer card.Disconnect(scard.ResetCard)

    // setup services and processor
    req := []byte{0x00, 0xC0, 0x00, 0x00}
    svc := NewScardService(card, req)
    cmds := NewCommandProvider()
    dec := charmap.Windows874.NewDecoder()
    decoder := charmap.Windows874.NewDecoder()
    writer := &OSFileWriter{}

    processor := NewCardProcessor(svc, cmds, dec, writer)
    svc := NewSmartCardService(card, cmds.reqPrefix)
    processor := NewCardProcessor(svc, cmds, decoder, writer)
    if err := processor.Process(); err != nil {
    fmt.Println("processing error:", err)
    fmt.Println("Processing error:", err)
    }
    }
    }
  2. bouroo revised this gist May 9, 2025. 1 changed file with 189 additions and 149 deletions.
    338 changes: 189 additions & 149 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -4,210 +4,250 @@ import (
    "bufio"
    "bytes"
    "fmt"
    "io/ioutil"
    "os"
    "strconv"

    "github.com/ebfe/scard"
    "golang.org/x/text/encoding/charmap"
    )

    const (
    cmdReqBase = 0x00
    cmdReqTail = 0x00
    cmdReqSelect = 0x01
    )
    // Interfaces

    var (
    cmdReq = []byte{cmdReqBase, 0xc0, cmdReqTail, cmdReqSelect}
    cmdSelectThaiCard = []byte{0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01}
    cmdCID = []byte{0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d}
    cmdTHFullname = []byte{0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64}
    cmdENFullname = []byte{0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64}
    cmdBirth = []byte{0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08}
    cmdGender = []byte{0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01}
    cmdIssuer = []byte{0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64}
    cmdIssueDate = []byte{0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08}
    cmdExpireDate = []byte{0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08}
    cmdAddress = []byte{0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64}
    cmdPhoto = generatePhotoCommands()
    )
    type ReaderSelector interface {
    Select([]string) (int, error)
    }

    func generatePhotoCommands() [][]byte {
    cmds := make([][]byte, 15)
    for i := 0; i < len(cmds); i++ {
    cmds[i] = []byte{0x80, 0xb0, byte(i + 0x01), byte(0x7B - i), 0x02, 0x00, 0xFF}
    }
    return cmds
    type CardConnector interface {
    Connect(*scard.Context, string) (*scard.Card, error)
    }

    func main() {
    context, err := scard.EstablishContext()
    if err != nil {
    fmt.Println("Error establishing context:", err)
    return
    }
    defer context.Release()
    type SmartCardService interface {
    Status() (scard.Status, error)
    ATR() ([]byte, error)
    AdjustRequest([]byte)
    Transmit([]byte) ([]byte, error)
    GetData([]byte) ([]byte, error)
    }

    readers, err := context.ListReaders()
    if err != nil {
    fmt.Println("Error listing readers:", err)
    return
    }
    type DataDecoder interface {
    Decode([]byte) ([]byte, error)
    }

    type FileWriter interface {
    Write(string, []byte) error
    }

    // Implementations

    // ConsoleReaderSelector prompts the user to pick a reader.
    type ConsoleReaderSelector struct{}

    func (s *ConsoleReaderSelector) Select(readers []string) (int, error) {
    if len(readers) == 0 {
    fmt.Println("No card readers found.")
    return
    return -1, fmt.Errorf("no readers")
    }

    fmt.Println("Available Readers:")
    for idx, reader := range readers {
    fmt.Printf("ID: %d, Reader: %s\n", idx, reader)
    if len(readers) == 1 {
    return 0, nil
    }

    readerIDx := selectReader(readers)
    if readerIDx < 0 {
    return // Error already printed in selectReader
    fmt.Println("Available readers:")
    for i, r := range readers {
    fmt.Printf("%d) %s\n", i, r)
    }

    card, err := connectToCard(context, readers[readerIDx])
    fmt.Print("Select reader [0]: ")
    line, err := bufio.NewReader(os.Stdin).ReadString('\n')
    if err != nil {
    fmt.Println("Error connecting to card:", err)
    return
    return 0, err
    }
    defer card.Disconnect(scard.ResetCard)

    if err := processCard(card); err != nil {
    fmt.Println("Error processing card:", err)
    n, err := strconv.Atoi(line[:len(line)-1])
    if err != nil || n < 0 || n >= len(readers) {
    return 0, nil
    }
    return n, nil
    }

    func selectReader(readers []string) int {
    readerIDx := 0
    if len(readers) > 1 {
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader ID [0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error reading input:", err)
    return -1
    }
    // ScardConnector wraps context.Connect.
    type ScardConnector struct{}

    var errInput error
    readerIDx, errInput = strconv.Atoi(readerIDxInput[:len(readerIDxInput)-1]) // Remove newline
    if errInput != nil || readerIDx < 0 || readerIDx >= len(readers) {
    fmt.Println("Error: Index out of bounds. Defaulting to 0.")
    readerIDx = 0
    }
    func (c *ScardConnector) Connect(ctx *scard.Context, reader string) (*scard.Card, error) {
    return ctx.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    }

    // scardService implements SmartCardService over scard.Card.
    type scardService struct {
    card *scard.Card
    reqPrefix []byte
    }

    func NewScardService(card *scard.Card, req []byte) SmartCardService {
    copyReq := make([]byte, len(req))
    copy(copyReq, req)
    return &scardService{card: card, reqPrefix: copyReq}
    }

    func (s *scardService) Status() (scard.Status, error) {
    return s.card.Status()
    }

    func (s *scardService) ATR() ([]byte, error) {
    return s.card.GetAttrib(scard.AttrAtrString)
    }

    func (s *scardService) AdjustRequest(atr []byte) {
    if len(atr) > 1 && atr[0] == 0x3B && atr[1] == 0x67 {
    s.reqPrefix[3] = 0x01
    }
    return readerIDx
    }

    func connectToCard(context *scard.Context, reader string) (*scard.Card, error) {
    card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    func (s *scardService) Transmit(cmd []byte) ([]byte, error) {
    return s.card.Transmit(cmd)
    }

    func (s *scardService) GetData(cmd []byte) ([]byte, error) {
    if _, err := s.card.Transmit(cmd); err != nil {
    return nil, err
    }
    tail := cmd[len(cmd)-1]
    resp, err := s.card.Transmit(append(s.reqPrefix, tail))
    if err != nil {
    return nil, err
    }
    return card, nil
    return resp[:len(resp)-2], nil
    }

    func processCard(card *scard.Card) error {
    status, err := card.Status()
    if err != nil {
    return fmt.Errorf("Error fetching card status: %w", err)
    // OSFileWriter writes bytes to disk.
    type OSFileWriter struct{}

    func (w *OSFileWriter) Write(path string, data []byte) error {
    return os.WriteFile(path, data, 0644)
    }

    // CommandProvider holds all APDUs.
    type CommandProvider struct {
    SelectThai []byte
    Infos []InfoCommand
    Photos [][]byte
    }

    type InfoCommand struct {
    Name string
    Cmd []byte
    }

    func NewCommandProvider() *CommandProvider {
    return &CommandProvider{
    SelectThai: []byte{0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01},
    Infos: []InfoCommand{
    {"CID", []byte{0x80, 0xB0, 0x00, 0x04, 0x02, 0x00, 0x0D}},
    {"ThaiFullname", []byte{0x80, 0xB0, 0x00, 0x11, 0x02, 0x00, 0x64}},
    {"EnglishFullname", []byte{0x80, 0xB0, 0x00, 0x75, 0x02, 0x00, 0x64}},
    {"Birthdate", []byte{0x80, 0xB0, 0x00, 0xD9, 0x02, 0x00, 0x08}},
    {"Gender", []byte{0x80, 0xB0, 0x00, 0xE1, 0x02, 0x00, 0x01}},
    {"Issuer", []byte{0x80, 0xB0, 0x00, 0xF6, 0x02, 0x00, 0x64}},
    {"IssueDate", []byte{0x80, 0xB0, 0x01, 0x67, 0x02, 0x00, 0x08}},
    {"ExpireDate", []byte{0x80, 0xB0, 0x01, 0x6F, 0x02, 0x00, 0x08}},
    {"Address", []byte{0x80, 0xB0, 0x15, 0x79, 0x02, 0x00, 0x64}},
    },
    Photos: func() [][]byte {
    out := make([][]byte, 15)
    for i := range out {
    out[i] = []byte{0x80, 0xB0, byte(i + 1), byte(0x7B - i), 0x02, 0x00, 0xFF}
    }
    return out
    }(),
    }
    fmt.Printf("Card status:\n\tReader: %s\n\tState: %x\n\tActive Protocol: %x\n\tATR: % x\n",
    status.Reader, status.State, status.ActiveProtocol, status.Atr)
    }

    atr, err := card.GetAttrib(scard.AttrAtrString)
    // CardProcessor orchestrates reads.
    type CardProcessor struct {
    svc SmartCardService
    cmds *CommandProvider
    decoder DataDecoder
    writer FileWriter
    }

    func NewCardProcessor(svc SmartCardService, cmds *CommandProvider, dec DataDecoder, w FileWriter) *CardProcessor {
    return &CardProcessor{svc: svc, cmds: cmds, decoder: dec, writer: w}
    }

    func (p *CardProcessor) Process() error {
    st, err := p.svc.Status()
    if err != nil {
    return fmt.Errorf("Error getting ATR: %w", err)
    return err
    }
    fmt.Printf("Reader: %s, State: %x, ATR: % X\n", st.Reader, st.State, st.Atr)

    fmt.Println("Card ATR:", string(atr))
    if atr[0] == 0x3B && atr[1] == 0x67 {
    cmdReq[3] = cmdReqSelect
    atr, err := p.svc.ATR()
    if err != nil {
    return err
    }
    p.svc.AdjustRequest(atr)

    fmt.Println("Request Command:", cmdReq)

    if err := card.Transmit(cmdSelectThaiCard); err != nil {
    return fmt.Errorf("Error selecting Thai ID card: %w", err)
    if _, err := p.svc.Transmit(p.cmds.SelectThai); err != nil {
    return err
    }

    infoCommands := []struct {
    cmd []byte
    name string
    }{
    {cmdCID, "CID"},
    {cmdTHFullname, "Thai Fullname"},
    {cmdENFullname, "English Fullname"},
    {cmdBirth, "Date of Birth"},
    {cmdGender, "Gender"},
    {cmdIssuer, "Issuer"},
    {cmdIssueDate, "Issue Date"},
    {cmdExpireDate, "Expire Date"},
    {cmdAddress, "Address"},
    for _, ic := range p.cmds.Infos {
    raw, err := p.svc.GetData(ic.Cmd)
    if err != nil {
    return err
    }
    dec, err := p.decoder.Decode(raw)
    if err != nil {
    return err
    }
    fmt.Printf("%s: %s\n", ic.Name, bytes.TrimSpace(dec))
    }

    for _, info := range infoCommands {
    resp, err := getString(card, info.cmd)
    // photo
    var img []byte
    for _, pc := range p.cmds.Photos {
    chunk, err := p.svc.GetData(pc)
    if err != nil {
    return fmt.Errorf("Error getting %s: %w", info.name, err)
    return err
    }
    fmt.Printf("%s: %s\n", info.name, resp)
    img = append(img, chunk...)
    }
    return p.writer.Write(p.cmds.Infos[0].Name+".jpg", img)
    }

    photo, err := getPhoto(card)
    func main() {
    ctx, err := scard.EstablishContext()
    if err != nil {
    return fmt.Errorf("Error getting photo: %w", err)
    fmt.Println(err)
    return
    }
    defer ctx.Release()

    if err := ioutil.WriteFile(fmt.Sprintf("%s.jpg", infoCommands[0].name), photo, 0644); err != nil {
    return fmt.Errorf("Error writing photo: %w", err)
    readers, err := ctx.ListReaders()
    if err != nil || len(readers) == 0 {
    fmt.Println("no readers")
    return
    }

    return nil
    }

    func getString(card *scard.Card, cmd []byte) (string, error) {
    rawResp, err := getData(card, cmd)
    sel := &ConsoleReaderSelector{}
    idx, err := sel.Select(readers)
    if err != nil {
    return "", err
    fmt.Println(err)
    return
    }

    thResp, err := thaiToUnicode(rawResp)
    conn := &ScardConnector{}
    card, err := conn.Connect(ctx, readers[idx])
    if err != nil {
    return "", err
    fmt.Println(err)
    return
    }
    defer card.Disconnect(scard.ResetCard)

    return string(bytes.TrimSpace(thResp)), nil
    }

    func thaiToUnicode(data []byte) ([]byte, error) {
    decoder := charmap.Windows874.NewDecoder()
    return decoder.Bytes(data)
    }

    func getData(card *scard.Card, cmd []byte) ([]byte, error) {
    if _, err := card.Transmit(cmd); err != nil {
    return nil, fmt.Errorf("Error transmitting command: %w", err)
    }
    resp, err := card.Transmit(append(cmdReq, cmd[len(cmd)-1]))
    if err != nil {
    return nil, fmt.Errorf("Error transmitting request: %w", err)
    }
    // Remove unused bytes (last two bytes)
    return resp[:len(resp)-2], nil
    }
    // setup services and processor
    req := []byte{0x00, 0xC0, 0x00, 0x00}
    svc := NewScardService(card, req)
    cmds := NewCommandProvider()
    dec := charmap.Windows874.NewDecoder()
    writer := &OSFileWriter{}

    func getPhoto(card *scard.Card) ([]byte, error) {
    var resp []byte
    for _, itemCmd := range cmdPhoto {
    tmpArray, err := getData(card, itemCmd)
    if err != nil {
    return nil, fmt.Errorf("Error getting photo data: %w", err)
    }
    resp = append(resp, tmpArray...)
    processor := NewCardProcessor(svc, cmds, dec, writer)
    if err := processor.Process(); err != nil {
    fmt.Println("processing error:", err)
    }
    return resp, nil
    }
    }
  3. bouroo revised this gist Jan 8, 2025. 1 changed file with 114 additions and 126 deletions.
    240 changes: 114 additions & 126 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,5 @@
    package main

    // Require pcscd, libpcsclite
    import (
    "bufio"
    "bytes"
    @@ -13,8 +12,14 @@ import (
    "golang.org/x/text/encoding/charmap"
    )

    const (
    cmdReqBase = 0x00
    cmdReqTail = 0x00
    cmdReqSelect = 0x01
    )

    var (
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x00}
    cmdReq = []byte{cmdReqBase, 0xc0, cmdReqTail, cmdReqSelect}
    cmdSelectThaiCard = []byte{0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01}
    cmdCID = []byte{0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d}
    cmdTHFullname = []byte{0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64}
    @@ -25,201 +30,184 @@ var (
    cmdIssueDate = []byte{0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08}
    cmdExpireDate = []byte{0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08}
    cmdAddress = []byte{0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64}
    cmdPhoto = [][]byte{
    {0x80, 0xb0, 0x01, 0x7B, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x02, 0x7A, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x03, 0x79, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x04, 0x78, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x05, 0x77, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x06, 0x76, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x07, 0x75, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x08, 0x74, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x09, 0x73, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0A, 0x72, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0B, 0x71, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0C, 0x70, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0D, 0x6F, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0E, 0x6E, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0F, 0x6D, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x10, 0x6C, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x11, 0x6B, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x12, 0x6A, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x13, 0x69, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x14, 0x68, 0x02, 0x00, 0xFF},
    }
    cmdPhoto = generatePhotoCommands()
    )

    func generatePhotoCommands() [][]byte {
    cmds := make([][]byte, 15)
    for i := 0; i < len(cmds); i++ {
    cmds[i] = []byte{0x80, 0xb0, byte(i + 0x01), byte(0x7B - i), 0x02, 0x00, 0xFF}
    }
    return cmds
    }

    func main() {
    // Create context with pcscd
    context, err := scard.EstablishContext()
    if err != nil {
    fmt.Println("Error EstablishContext:", err)
    fmt.Println("Error establishing context:", err)
    return
    }

    // Release context after exit
    defer context.Release()

    // Get all reader
    readers, err := context.ListReaders()
    if err != nil {
    fmt.Println("Error ListReaders:", err)
    fmt.Println("Error listing readers:", err)
    return
    }
    if len(readers) == 0 {
    fmt.Println("Error card readers not found.")
    fmt.Println("No card readers found.")
    return
    }

    fmt.Println("Readers: ")
    for rIdx, rItem := range readers {
    fmt.Println("Card reader ID: ", rIdx, "Item: ", rItem)
    fmt.Println("Available Readers:")
    for idx, reader := range readers {
    fmt.Printf("ID: %d, Reader: %s\n", idx, reader)
    }

    readerIDx := selectReader(readers)
    if readerIDx < 0 {
    return // Error already printed in selectReader
    }

    // Select reader
    card, err := connectToCard(context, readers[readerIDx])
    if err != nil {
    fmt.Println("Error connecting to card:", err)
    return
    }
    defer card.Disconnect(scard.ResetCard)

    if err := processCard(card); err != nil {
    fmt.Println("Error processing card:", err)
    }
    }

    func selectReader(readers []string) int {
    readerIDx := 0
    if len(readers) > 1 {
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader ID[0]: ")
    fmt.Print("Select card reader ID [0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error select reader:", err)
    return
    fmt.Println("Error reading input:", err)
    return -1
    }
    fmt.Println("Selected: ", readerIDxInput)
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if err != nil {

    var errInput error
    readerIDx, errInput = strconv.Atoi(readerIDxInput[:len(readerIDxInput)-1]) // Remove newline
    if errInput != nil || readerIDx < 0 || readerIDx >= len(readers) {
    fmt.Println("Error: Index out of bounds. Defaulting to 0.")
    readerIDx = 0
    }
    if readerIDx < 0 || readerIDx > len(readers)-1 {
    fmt.Println("Error select reader: index out of bound")
    return
    }
    }
    reader := readers[readerIDx]
    return readerIDx
    }

    // Connect to card
    func connectToCard(context *scard.Context, reader string) (*scard.Card, error) {
    card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    if err != nil {
    fmt.Println("Error Connect:", err)
    return
    return nil, err
    }
    return card, nil
    }

    // Disconnect from card after exit
    defer card.Disconnect(scard.ResetCard)

    fmt.Println("Card status:")
    func processCard(card *scard.Card) error {
    status, err := card.Status()
    if err != nil {
    fmt.Println("Error status:", err)
    return
    return fmt.Errorf("Error fetching card status: %w", err)
    }
    fmt.Printf("\treader: %s\n\tstate: %x\n\tactive protocol: %x\n\tatr: % x\n",
    fmt.Printf("Card status:\n\tReader: %s\n\tState: %x\n\tActive Protocol: %x\n\tATR: % x\n",
    status.Reader, status.State, status.ActiveProtocol, status.Atr)

    atr, err := card.GetAttrib(scard.AttrAtrString)
    if err != nil {
    fmt.Println("Error GetAttrib:", err)
    return
    return fmt.Errorf("Error getting ATR: %w", err)
    }

    // Get card attribute
    fmt.Println("Card ATR: ", string(atr))
    fmt.Println("Card ATR:", string(atr))
    if atr[0] == 0x3B && atr[1] == 0x67 {
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x01}
    } else {
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x00}
    cmdReq[3] = cmdReqSelect
    }
    fmt.Println("Req: ", cmdReq)

    // Select thai national ID card
    card.Transmit(cmdSelectThaiCard)

    cid, _ := getString(card, cmdCID, cmdReq)
    fmt.Println("cid: ", cid)

    thFullname, _ := getString(card, cmdTHFullname, cmdReq)
    fmt.Println("thFullname: ", thFullname)
    fmt.Println("Request Command:", cmdReq)

    enFullname, _ := getString(card, cmdENFullname, cmdReq)
    fmt.Println("enFullname: ", enFullname)

    dateOfBirth, _ := getString(card, cmdBirth, cmdReq)
    fmt.Println("dateOfBirth: ", dateOfBirth)

    gender, _ := getString(card, cmdGender, cmdReq)
    fmt.Println("gender: ", gender)

    issuer, _ := getString(card, cmdIssuer, cmdReq)
    fmt.Println("issuer: ", issuer)

    issueDate, _ := getString(card, cmdIssueDate, cmdReq)
    fmt.Println("issueDate: ", issueDate)
    if err := card.Transmit(cmdSelectThaiCard); err != nil {
    return fmt.Errorf("Error selecting Thai ID card: %w", err)
    }

    expireDate, _ := getString(card, cmdExpireDate, cmdReq)
    fmt.Println("expireDate: ", expireDate)
    infoCommands := []struct {
    cmd []byte
    name string
    }{
    {cmdCID, "CID"},
    {cmdTHFullname, "Thai Fullname"},
    {cmdENFullname, "English Fullname"},
    {cmdBirth, "Date of Birth"},
    {cmdGender, "Gender"},
    {cmdIssuer, "Issuer"},
    {cmdIssueDate, "Issue Date"},
    {cmdExpireDate, "Expire Date"},
    {cmdAddress, "Address"},
    }

    address, _ := getString(card, cmdAddress, cmdReq)
    fmt.Println("address: ", address)
    for _, info := range infoCommands {
    resp, err := getString(card, info.cmd)
    if err != nil {
    return fmt.Errorf("Error getting %s: %w", info.name, err)
    }
    fmt.Printf("%s: %s\n", info.name, resp)
    }

    photo, _ := getPhoto(card, cmdReq)
    err = ioutil.WriteFile(cid+".jpg", photo, 0664)
    photo, err := getPhoto(card)
    if err != nil {
    fmt.Printf("Error write photo: %+v", err)
    return
    return fmt.Errorf("Error getting photo: %w", err)
    }

    if err := ioutil.WriteFile(fmt.Sprintf("%s.jpg", infoCommands[0].name), photo, 0644); err != nil {
    return fmt.Errorf("Error writing photo: %w", err)
    }

    return nil
    }

    func getString(card *scard.Card, cmd, req []byte) (resp string, err error) {
    rawResp, err := getData(card, cmd, cmdReq)
    func getString(card *scard.Card, cmd []byte) (string, error) {
    rawResp, err := getData(card, cmd)
    if err != nil {
    fmt.Printf("getString: %+v", err)
    return resp, err
    return "", err
    }

    thResp, err := thaiToUnicode(rawResp)
    if err != nil {
    fmt.Printf("getString: %+v", err)
    return resp, err
    return "", err
    }

    // Remove unused bytes
    thResp = bytes.Trim(thResp, " ")

    return string(thResp), err
    return string(bytes.TrimSpace(thResp)), nil
    }

    func thaiToUnicode(data []byte) (out []byte, err error) {
    func thaiToUnicode(data []byte) ([]byte, error) {
    decoder := charmap.Windows874.NewDecoder()
    out, err = decoder.Bytes(data)
    return out, err
    return decoder.Bytes(data)
    }

    func getData(card *scard.Card, cmd, req []byte) (resp []byte, err error) {
    // Send cmd
    _, err = card.Transmit(cmd)
    if err != nil {
    fmt.Printf("getData: %+v", err)
    return resp, err
    func getData(card *scard.Card, cmd []byte) ([]byte, error) {
    if _, err := card.Transmit(cmd); err != nil {
    return nil, fmt.Errorf("Error transmitting command: %w", err)
    }
    // Send select cmd
    req = append(req, cmd[len(cmd)-1])
    resp, err = card.Transmit(req)
    resp, err := card.Transmit(append(cmdReq, cmd[len(cmd)-1]))
    if err != nil {
    fmt.Printf("getData: %+v", err)
    return resp, err
    return nil, fmt.Errorf("Error transmitting request: %w", err)
    }
    // Remove unused bytes
    resp = resp[:len(resp)-2]
    return resp, err
    // Remove unused bytes (last two bytes)
    return resp[:len(resp)-2], nil
    }

    func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    func getPhoto(card *scard.Card) ([]byte, error) {
    var resp []byte
    for _, itemCmd := range cmdPhoto {
    tmpArray, err := getData(card, itemCmd, req)
    tmpArray, err := getData(card, itemCmd)
    if err != nil {
    fmt.Printf("getPhoto: %+v", err)
    return nil, fmt.Errorf("Error getting photo data: %w", err)
    }
    resp = append(resp, tmpArray...)
    }
    return resp, err
    }
    return resp, nil
    }
  4. bouroo revised this gist May 11, 2021. 1 changed file with 11 additions and 3 deletions.
    14 changes: 11 additions & 3 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -97,7 +97,6 @@ func main() {
    }
    }
    reader := readers[readerIDx]
    fmt.Println("Using reader:", reader)

    // Connect to card
    card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    @@ -107,7 +106,16 @@ func main() {
    }

    // Disconnect from card after exit
    defer card.Disconnect(scard.LeaveCard)
    defer card.Disconnect(scard.ResetCard)

    fmt.Println("Card status:")
    status, err := card.Status()
    if err != nil {
    fmt.Println("Error status:", err)
    return
    }
    fmt.Printf("\treader: %s\n\tstate: %x\n\tactive protocol: %x\n\tatr: % x\n",
    status.Reader, status.State, status.ActiveProtocol, status.Atr)

    atr, err := card.GetAttrib(scard.AttrAtrString)
    if err != nil {
    @@ -214,4 +222,4 @@ func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    resp = append(resp, tmpArray...)
    }
    return resp, err
    }
    }
  5. bouroo revised this gist May 11, 2021. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -88,6 +88,9 @@ func main() {
    }
    fmt.Println("Selected: ", readerIDxInput)
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if err != nil {
    readerIDx = 0
    }
    if readerIDx < 0 || readerIDx > len(readers)-1 {
    fmt.Println("Error select reader: index out of bound")
    return
    @@ -208,9 +211,7 @@ func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    if err != nil {
    fmt.Printf("getPhoto: %+v", err)
    }
    for _, tmpItem := range tmpArray {
    resp = append(resp, tmpItem)
    }
    resp = append(resp, tmpArray...)
    }
    return resp, err
    }
    }
  6. bouroo revised this gist Sep 28, 2018. 1 changed file with 16 additions and 12 deletions.
    28 changes: 16 additions & 12 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -77,17 +77,21 @@ func main() {
    }

    // Select reader
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader ID[0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error select reader:", err)
    return
    }
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if readerIDx < 0 || readerIDx > len(readers)-1 {
    fmt.Println("Error select reader: index out of bound")
    return
    readerIDx := 0
    if len(readers) > 1 {
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader ID[0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error select reader:", err)
    return
    }
    fmt.Println("Selected: ", readerIDxInput)
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if readerIDx < 0 || readerIDx > len(readers)-1 {
    fmt.Println("Error select reader: index out of bound")
    return
    }
    }
    reader := readers[readerIDx]
    fmt.Println("Using reader:", reader)
    @@ -209,4 +213,4 @@ func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    }
    }
    return resp, err
    }
    }
  7. bouroo revised this gist Sep 27, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,7 @@ func main() {
    return
    }
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if readerIDx < 0 || readerIDx > len(readers) {
    if readerIDx < 0 || readerIDx > len(readers)-1 {
    fmt.Println("Error select reader: index out of bound")
    return
    }
    @@ -209,4 +209,4 @@ func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    }
    }
    return resp, err
    }
    }
  8. bouroo revised this gist Sep 27, 2018. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -85,6 +85,10 @@ func main() {
    return
    }
    readerIDx, err := strconv.Atoi(readerIDxInput)
    if readerIDx < 0 || readerIDx > len(readers) {
    fmt.Println("Error select reader: index out of bound")
    return
    }
    reader := readers[readerIDx]
    fmt.Println("Using reader:", reader)

    @@ -205,4 +209,4 @@ func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    }
    }
    return resp, err
    }
    }
  9. bouroo revised this gist Sep 27, 2018. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -73,12 +73,12 @@ func main() {

    fmt.Println("Readers: ")
    for rIdx, rItem := range readers {
    fmt.Println("ID: ", rIdx, "Item: ", rItem)
    fmt.Println("Card reader ID: ", rIdx, "Item: ", rItem)
    }

    // Select reader
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader[0]: ")
    fmt.Print("Select card reader ID[0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error select reader:", err)
    @@ -163,8 +163,7 @@ func getString(card *scard.Card, cmd, req []byte) (resp string, err error) {
    return resp, err
    }

    // Remove unused char
    thResp = thResp[:len(thResp)-4]
    // Remove unused bytes
    thResp = bytes.Trim(thResp, " ")

    return string(thResp), err
    @@ -190,6 +189,8 @@ func getData(card *scard.Card, cmd, req []byte) (resp []byte, err error) {
    fmt.Printf("getData: %+v", err)
    return resp, err
    }
    // Remove unused bytes
    resp = resp[:len(resp)-2]
    return resp, err
    }

  10. bouroo revised this gist Sep 27, 2018. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@ package main
    // Require pcscd, libpcsclite
    import (
    "bufio"
    "bytes"
    "fmt"
    "io/ioutil"
    "os"
    @@ -162,7 +163,9 @@ func getString(card *scard.Card, cmd, req []byte) (resp string, err error) {
    return resp, err
    }

    thResp = thResp[:len(thResp)-1]
    // Remove unused char
    thResp = thResp[:len(thResp)-4]
    thResp = bytes.Trim(thResp, " ")

    return string(thResp), err
    }
  11. bouroo revised this gist Sep 27, 2018. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -174,11 +174,13 @@ func thaiToUnicode(data []byte) (out []byte, err error) {
    }

    func getData(card *scard.Card, cmd, req []byte) (resp []byte, err error) {
    // Send cmd
    _, err = card.Transmit(cmd)
    if err != nil {
    fmt.Printf("getData: %+v", err)
    return resp, err
    }
    // Send select cmd
    req = append(req, cmd[len(cmd)-1])
    resp, err = card.Transmit(req)
    if err != nil {
  12. bouroo revised this gist Sep 27, 2018. 1 changed file with 139 additions and 19 deletions.
    158 changes: 139 additions & 19 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,30 @@
    package main

    // Require pcscd, libpcsclite
    import (
    "bufio"
    "fmt"
    "io/ioutil"
    "os"
    "strconv"

    "github.com/ebfe/scard"
    "golang.org/x/text/encoding/charmap"
    )

    var (
    cmdSelect = []byte{0x00, 0xA4, 0x04, 0x00, 0x08}
    cmdThaiCard = []byte{0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01}
    cmdCID = []byte{0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d}
    cmdTHFullname = []byte{0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64}
    cmdENFullname = []byte{0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64}
    cmdBirth = []byte{0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08}
    cmdGender = []byte{0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01}
    cmdIssuer = []byte{0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64}
    cmdIssueDate = []byte{0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08}
    cmdExpireDate = []byte{0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08}
    cmdAddress = []byte{0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64}
    cmdPhoto = [][]byte{
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x00}
    cmdSelectThaiCard = []byte{0x00, 0xA4, 0x04, 0x00, 0x08, 0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01}
    cmdCID = []byte{0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d}
    cmdTHFullname = []byte{0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64}
    cmdENFullname = []byte{0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64}
    cmdBirth = []byte{0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08}
    cmdGender = []byte{0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01}
    cmdIssuer = []byte{0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64}
    cmdIssueDate = []byte{0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08}
    cmdExpireDate = []byte{0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08}
    cmdAddress = []byte{0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64}
    cmdPhoto = [][]byte{
    {0x80, 0xb0, 0x01, 0x7B, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x02, 0x7A, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x03, 0x79, 0x02, 0x00, 0xFF},
    @@ -44,39 +49,154 @@ var (
    )

    func main() {
    // Create context with pcscd
    context, err := scard.EstablishContext()
    if err != nil {
    fmt.Println("Error EstablishContext:", err)
    return
    }

    // Release context after exit
    defer context.Release()

    // Get all reader
    readers, err := context.ListReaders()
    if err != nil {
    fmt.Println("Error ListReaders:", err)
    return
    }
    if len(readers) == 0 {
    fmt.Println("Error card readers not found.")
    return
    }

    fmt.Println("Readers: ")
    for rIdx, rItem := range readers {
    fmt.Println("ID: ", rIdx, "Item: ", rItem)
    }

    reader := readers[0]
    // Select reader
    buf := bufio.NewReader(os.Stdin)
    fmt.Print("Select card reader[0]: ")
    readerIDxInput, err := buf.ReadString('\n')
    if err != nil {
    fmt.Println("Error select reader:", err)
    return
    }
    readerIDx, err := strconv.Atoi(readerIDxInput)
    reader := readers[readerIDx]
    fmt.Println("Using reader:", reader)

    // Connect to card
    card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    if err != nil {
    fmt.Println("Error Connect:", err)
    return
    }

    // Disconnect from card after exit
    defer card.Disconnect(scard.LeaveCard)

    atr, err := card.GetAttrib(scard.AttrAtrString)
    if err != nil {
    fmt.Println("Error GetAttrib:", err)
    return
    }

    // Get card attribute
    fmt.Println("Card ATR: ", string(atr))
    if atr[0] == 0x3B && atr[1] == 0x67 {
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x01}
    } else {
    cmdReq = []byte{0x00, 0xc0, 0x00, 0x00}
    }
    fmt.Println("Req: ", cmdReq)

    // Select thai national ID card
    card.Transmit(cmdSelectThaiCard)

    cid, _ := getString(card, cmdCID, cmdReq)
    fmt.Println("cid: ", cid)

    thFullname, _ := getString(card, cmdTHFullname, cmdReq)
    fmt.Println("thFullname: ", thFullname)

    enFullname, _ := getString(card, cmdENFullname, cmdReq)
    fmt.Println("enFullname: ", enFullname)

    dateOfBirth, _ := getString(card, cmdBirth, cmdReq)
    fmt.Println("dateOfBirth: ", dateOfBirth)

    gender, _ := getString(card, cmdGender, cmdReq)
    fmt.Println("gender: ", gender)

    issuer, _ := getString(card, cmdIssuer, cmdReq)
    fmt.Println("issuer: ", issuer)

    issueDate, _ := getString(card, cmdIssueDate, cmdReq)
    fmt.Println("issueDate: ", issueDate)

    expireDate, _ := getString(card, cmdExpireDate, cmdReq)
    fmt.Println("expireDate: ", expireDate)

    address, _ := getString(card, cmdAddress, cmdReq)
    fmt.Println("address: ", address)

    photo, _ := getPhoto(card, cmdReq)
    err = ioutil.WriteFile(cid+".jpg", photo, 0664)
    if err != nil {
    fmt.Printf("Error write photo: %+v", err)
    return
    }
    }

    func getString(card *scard.Card, cmd, req []byte) (resp string, err error) {
    rawResp, err := getData(card, cmd, cmdReq)
    if err != nil {
    fmt.Printf("getString: %+v", err)
    return resp, err
    }
    thResp, err := thaiToUnicode(rawResp)
    if err != nil {
    fmt.Printf("getString: %+v", err)
    return resp, err
    }

    thResp = thResp[:len(thResp)-1]

    return string(thResp), err
    }

    func thaiToUnicode(data []byte) (string, error) {
    dec := charmap.Windows874.NewDecoder()
    out, err := dec.Bytes(data)
    return string(out), err
    func thaiToUnicode(data []byte) (out []byte, err error) {
    decoder := charmap.Windows874.NewDecoder()
    out, err = decoder.Bytes(data)
    return out, err
    }

    func getData(cmd, req []byte) {
    func getData(card *scard.Card, cmd, req []byte) (resp []byte, err error) {
    _, err = card.Transmit(cmd)
    if err != nil {
    fmt.Printf("getData: %+v", err)
    return resp, err
    }
    req = append(req, cmd[len(cmd)-1])
    resp, err = card.Transmit(req)
    if err != nil {
    fmt.Printf("getData: %+v", err)
    return resp, err
    }
    return resp, err
    }

    }
    func getPhoto(card *scard.Card, req []byte) (resp []byte, err error) {
    for _, itemCmd := range cmdPhoto {
    tmpArray, err := getData(card, itemCmd, req)
    if err != nil {
    fmt.Printf("getPhoto: %+v", err)
    }
    for _, tmpItem := range tmpArray {
    resp = append(resp, tmpItem)
    }
    }
    return resp, err
    }
  13. bouroo created this gist Sep 27, 2018.
    82 changes: 82 additions & 0 deletions thai-id-card.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    package main

    import (
    "fmt"

    "github.com/ebfe/scard"
    "golang.org/x/text/encoding/charmap"
    )

    var (
    cmdSelect = []byte{0x00, 0xA4, 0x04, 0x00, 0x08}
    cmdThaiCard = []byte{0xA0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01}
    cmdCID = []byte{0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d}
    cmdTHFullname = []byte{0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64}
    cmdENFullname = []byte{0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64}
    cmdBirth = []byte{0x80, 0xb0, 0x00, 0xD9, 0x02, 0x00, 0x08}
    cmdGender = []byte{0x80, 0xb0, 0x00, 0xE1, 0x02, 0x00, 0x01}
    cmdIssuer = []byte{0x80, 0xb0, 0x00, 0xF6, 0x02, 0x00, 0x64}
    cmdIssueDate = []byte{0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08}
    cmdExpireDate = []byte{0x80, 0xb0, 0x01, 0x6F, 0x02, 0x00, 0x08}
    cmdAddress = []byte{0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64}
    cmdPhoto = [][]byte{
    {0x80, 0xb0, 0x01, 0x7B, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x02, 0x7A, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x03, 0x79, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x04, 0x78, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x05, 0x77, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x06, 0x76, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x07, 0x75, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x08, 0x74, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x09, 0x73, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0A, 0x72, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0B, 0x71, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0C, 0x70, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0D, 0x6F, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0E, 0x6E, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x0F, 0x6D, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x10, 0x6C, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x11, 0x6B, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x12, 0x6A, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x13, 0x69, 0x02, 0x00, 0xFF},
    {0x80, 0xb0, 0x14, 0x68, 0x02, 0x00, 0xFF},
    }
    )

    func main() {
    context, err := scard.EstablishContext()
    if err != nil {
    fmt.Println("Error EstablishContext:", err)
    return
    }

    defer context.Release()

    readers, err := context.ListReaders()
    if err != nil {
    fmt.Println("Error ListReaders:", err)
    return
    }

    reader := readers[0]
    fmt.Println("Using reader:", reader)

    card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny)
    if err != nil {
    fmt.Println("Error Connect:", err)
    return
    }

    defer card.Disconnect(scard.LeaveCard)

    }

    func thaiToUnicode(data []byte) (string, error) {
    dec := charmap.Windows874.NewDecoder()
    out, err := dec.Bytes(data)
    return string(out), err
    }

    func getData(cmd, req []byte) {

    }