Last active
July 8, 2025 08:51
-
-
Save bouroo/a3aa857727d909283c33e7b00623b48d to your computer and use it in GitHub Desktop.
Revisions
-
bouroo revised this gist
Jun 1, 2025 . 1 changed file with 34 additions and 33 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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(cmd []byte) ([]byte, error) } type DataDecoder interface { Decode([]byte) ([]byte, error) } type FileWriter interface { 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) } // SmartCardService implementation for scard.Card type smartCardService struct { card *scard.Card reqPrefix []byte } func NewSmartCardService(card *scard.Card, req []byte) SmartCardService { copyReq := make([]byte, len(req)) copy(copyReq, req) return &smartCardService{card: card, reqPrefix: copyReq} } func (s *smartCardService) Status() (scard.Status, error) { return s.card.Status() } func (s *smartCardService) ATR() ([]byte, error) { return s.card.GetAttrib(scard.AttrAtrString) } func (s *smartCardService) AdjustRequest(atr []byte) { if len(atr) > 1 && atr[0] == 0x3B && atr[1] == 0x67 { s.reqPrefix[3] = 0x01 } } func (s *smartCardService) Transmit(cmd []byte) ([]byte, error) { return s.card.Transmit(cmd) } 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 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 } decoded, err := p.decoder.Decode(raw) if err != nil { return err } 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("output.jpg", img) } func main() { ctx, err := scard.EstablishContext() if err != nil { fmt.Println("Failed to establish context:", err) return } defer ctx.Release() readers, err := ctx.ListReaders() if err != nil { fmt.Println("Failed to list readers:", err) return } idx, err := (&ConsoleReaderSelector{}).Select(readers) if err != nil { fmt.Println("Failed to select reader:", err) return } connector := &ScardConnector{} card, err := connector.Connect(ctx, readers[idx]) if err != nil { fmt.Println("Failed to connect to card:", err) return } defer card.Disconnect(scard.ResetCard) cmds := NewCommandProvider() decoder := charmap.Windows874.NewDecoder() writer := &OSFileWriter{} svc := NewSmartCardService(card, cmds.reqPrefix) processor := NewCardProcessor(svc, cmds, decoder, writer) if err := processor.Process(); err != nil { fmt.Println("Processing error:", err) } } -
bouroo revised this gist
May 9, 2025 . 1 changed file with 189 additions and 149 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,210 +4,250 @@ import ( "bufio" "bytes" "fmt" "os" "strconv" "github.com/ebfe/scard" "golang.org/x/text/encoding/charmap" ) // Interfaces type ReaderSelector interface { Select([]string) (int, error) } type CardConnector interface { Connect(*scard.Context, string) (*scard.Card, error) } type SmartCardService interface { Status() (scard.Status, error) ATR() ([]byte, error) AdjustRequest([]byte) Transmit([]byte) ([]byte, error) GetData([]byte) ([]byte, error) } 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 { return -1, fmt.Errorf("no readers") } if len(readers) == 1 { return 0, nil } fmt.Println("Available readers:") for i, r := range readers { fmt.Printf("%d) %s\n", i, r) } fmt.Print("Select reader [0]: ") line, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { return 0, err } n, err := strconv.Atoi(line[:len(line)-1]) if err != nil || n < 0 || n >= len(readers) { return 0, nil } return n, nil } // ScardConnector wraps context.Connect. type ScardConnector struct{} 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 } } 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 resp[:len(resp)-2], nil } // 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 }(), } } // 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 err } fmt.Printf("Reader: %s, State: %x, ATR: % X\n", st.Reader, st.State, st.Atr) atr, err := p.svc.ATR() if err != nil { return err } p.svc.AdjustRequest(atr) if _, err := p.svc.Transmit(p.cmds.SelectThai); err != nil { return err } 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)) } // photo var img []byte for _, pc := range p.cmds.Photos { chunk, err := p.svc.GetData(pc) if err != nil { return err } img = append(img, chunk...) } return p.writer.Write(p.cmds.Infos[0].Name+".jpg", img) } func main() { ctx, err := scard.EstablishContext() if err != nil { fmt.Println(err) return } defer ctx.Release() readers, err := ctx.ListReaders() if err != nil || len(readers) == 0 { fmt.Println("no readers") return } sel := &ConsoleReaderSelector{} idx, err := sel.Select(readers) if err != nil { fmt.Println(err) return } conn := &ScardConnector{} card, err := conn.Connect(ctx, readers[idx]) if err != nil { fmt.Println(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() writer := &OSFileWriter{} processor := NewCardProcessor(svc, cmds, dec, writer) if err := processor.Process(); err != nil { fmt.Println("processing error:", err) } } -
bouroo revised this gist
Jan 8, 2025 . 1 changed file with 114 additions and 126 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,5 @@ package main import ( "bufio" "bytes" @@ -13,8 +12,14 @@ import ( "golang.org/x/text/encoding/charmap" ) const ( cmdReqBase = 0x00 cmdReqTail = 0x00 cmdReqSelect = 0x01 ) 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} @@ -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 = 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() { context, err := scard.EstablishContext() if err != nil { fmt.Println("Error establishing context:", err) return } defer context.Release() readers, err := context.ListReaders() if err != nil { fmt.Println("Error listing readers:", err) return } if len(readers) == 0 { fmt.Println("No card readers found.") return } 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 } 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]: ") readerIDxInput, err := buf.ReadString('\n') if err != nil { fmt.Println("Error reading input:", err) return -1 } 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 } } return readerIDx } func connectToCard(context *scard.Context, reader string) (*scard.Card, error) { card, err := context.Connect(reader, scard.ShareShared, scard.ProtocolAny) if err != nil { return nil, err } return card, nil } func processCard(card *scard.Card) error { status, err := card.Status() if err != nil { return fmt.Errorf("Error fetching card status: %w", err) } 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 { return fmt.Errorf("Error getting ATR: %w", err) } fmt.Println("Card ATR:", string(atr)) if atr[0] == 0x3B && atr[1] == 0x67 { cmdReq[3] = cmdReqSelect } fmt.Println("Request Command:", cmdReq) if err := card.Transmit(cmdSelectThaiCard); err != nil { return fmt.Errorf("Error selecting Thai ID card: %w", 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 _, 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, err := getPhoto(card) if err != nil { 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 []byte) (string, error) { rawResp, err := getData(card, cmd) if err != nil { return "", err } thResp, err := thaiToUnicode(rawResp) if err != nil { return "", err } 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 } 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...) } return resp, nil } -
bouroo revised this gist
May 11, 2021 . 1 changed file with 11 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -97,7 +97,6 @@ func main() { } } reader := readers[readerIDx] // 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.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 } -
bouroo revised this gist
May 11, 2021 . 1 changed file with 5 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) } resp = append(resp, tmpArray...) } return resp, err } -
bouroo revised this gist
Sep 28, 2018 . 1 changed file with 16 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -77,17 +77,21 @@ func main() { } // Select reader 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 } -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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)-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 } -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 5 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 } -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 5 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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("Card reader ID: ", rIdx, "Item: ", rItem) } // 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) @@ -163,8 +163,7 @@ func getString(card *scard.Card, cmd, req []byte) (resp string, err error) { return resp, err } // 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 } -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 } // Remove unused char thResp = thResp[:len(thResp)-4] thResp = bytes.Trim(thResp, " ") return string(thResp), err } -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 { -
bouroo revised this gist
Sep 27, 2018 . 1 changed file with 139 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 ( 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) } // 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) (out []byte, err error) { decoder := charmap.Windows874.NewDecoder() out, err = decoder.Bytes(data) return out, err } 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 } -
bouroo created this gist
Sep 27, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) { }