You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
4.0 KiB
206 lines
4.0 KiB
package nfc
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/ebfe/scard"
|
|
)
|
|
|
|
var ctx *scard.Context
|
|
var readers []string
|
|
|
|
func die(err error) {
|
|
fmt.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func initReader() error {
|
|
var err error
|
|
// Establish a context
|
|
ctx, err = scard.EstablishContext()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// List available readers
|
|
readers, err = ctx.ListReaders()
|
|
if err != nil {
|
|
return errors.New("讀卡機未連接")
|
|
}
|
|
|
|
fmt.Printf("Found %d readers:\n", len(readers))
|
|
for i, reader := range readers {
|
|
fmt.Printf("[%d] %s\n", i, reader)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetNfcId() (string, error) {
|
|
err := initReader()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(readers) > 0 {
|
|
//Connect to card
|
|
fmt.Println("Connecting to card in ", readers[0])
|
|
|
|
card, err := ctx.Connect(readers[0], scard.ShareExclusive, scard.ProtocolAny)
|
|
if err != nil {
|
|
return "", errors.New("請將卡片放置讀卡機上")
|
|
}
|
|
defer card.Disconnect(scard.ResetCard)
|
|
|
|
fmt.Println("Card status:")
|
|
status, err := card.Status()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fmt.Printf("\treader: %s\n\tstate: %x\n\tactive protocol: %x\n\tatr: % x\n",
|
|
status.Reader, status.State, status.ActiveProtocol, status.Atr)
|
|
|
|
apdu := []byte{0xFF, 0xCA, 0x00, 0x00, 0x00}
|
|
rsp, err := card.Transmit(apdu)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Parse UID from response
|
|
if len(rsp) >= 3 && rsp[len(rsp)-2] == 0x90 && rsp[len(rsp)-1] == 0x00 {
|
|
uid := rsp[:len(rsp)-2]
|
|
//將uid reverse
|
|
reverseBytes(uid)
|
|
return strings.ToUpper(fmt.Sprintf("%x", uid)), nil
|
|
}
|
|
|
|
return "NFC ID", nil
|
|
}
|
|
|
|
return "", fmt.Errorf("No NFC reader found")
|
|
}
|
|
|
|
func WriteNfcCard(data string) (string, error) {
|
|
err := initReader()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(readers) > 0 {
|
|
// Connect to card
|
|
fmt.Println("Connecting to card in ", readers[0])
|
|
card, err := ctx.Connect(readers[0], scard.ShareExclusive, scard.ProtocolAny)
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer card.Disconnect(scard.ResetCard)
|
|
|
|
fmt.Println("Card status:")
|
|
status, err := card.Status()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fmt.Printf("\treader: %s\n\tstate: %x\n\tactive protocol: %x\n\tatr: % x\n",
|
|
status.Reader, status.State, status.ActiveProtocol, status.Atr)
|
|
|
|
url := []byte(data)
|
|
|
|
ndef := []byte{
|
|
0x03, byte(len(url) + 5), 0xd1, 0x01,
|
|
byte(len(url) + 1), 0x55, 0x04,
|
|
}
|
|
|
|
//將url加入ndef,最後以0xef結尾
|
|
ndef = append(ndef, url...)
|
|
ndef = append(ndef, 0xfe)
|
|
// for _, b := range ndef {
|
|
// fmt.Printf("%02x ", b)
|
|
// }
|
|
// fmt.Println()
|
|
|
|
//將ndef 4bytes一組
|
|
groupSize := 4
|
|
ndefSize := len(ndef)
|
|
groupCount := ndefSize / groupSize
|
|
if ndefSize%groupSize != 0 {
|
|
groupCount++
|
|
}
|
|
|
|
//將ndef分組
|
|
groups := make([][4]byte, groupCount)
|
|
for i := 0; i < groupCount; i++ {
|
|
start := i * groupSize
|
|
end := start + groupSize
|
|
if end > ndefSize {
|
|
end = ndefSize
|
|
}
|
|
copy(groups[i][:], ndef[start:end])
|
|
}
|
|
|
|
//將ndef分組後的資料寫入卡片
|
|
for i, group := range groups {
|
|
|
|
var cmd = []byte{
|
|
0xFF, 0xD6,
|
|
0x00,
|
|
byte(4 + i),
|
|
0x04,
|
|
}
|
|
|
|
cmd = append(cmd, group[:]...)
|
|
|
|
fmt.Println(cmd)
|
|
fmt.Println("Transmit:")
|
|
fmt.Printf("\tc-apdu: % x\n", cmd)
|
|
rsp, err := card.Transmit(cmd)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
fmt.Printf("\tr-apdu: % x\n", rsp)
|
|
}
|
|
|
|
apdu := []byte{0xFF, 0x00, 0x52, 0xFF, 0xFF}
|
|
rsp, err := card.Transmit(apdu)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
fmt.Println()
|
|
fmt.Printf("\tr-apdu: % x\n", rsp)
|
|
fmt.Println("Done")
|
|
}
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func waitUntilCardPresent() (int, error) {
|
|
rs := make([]scard.ReaderState, len(readers))
|
|
for i := range rs {
|
|
rs[i].Reader = readers[i]
|
|
rs[i].CurrentState = scard.StateUnaware
|
|
}
|
|
|
|
for {
|
|
for i := range rs {
|
|
if rs[i].EventState&scard.StatePresent != 0 {
|
|
return i, nil
|
|
}
|
|
rs[i].CurrentState = rs[i].EventState
|
|
}
|
|
err := ctx.GetStatusChange(rs, -1)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
}
|
|
}
|
|
|
|
func reverseBytes(slice []byte) {
|
|
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
|
|
slice[i], slice[j] = slice[j], slice[i]
|
|
}
|
|
}
|