嵌入式linux之go语言开发(三)卡库的封装

简介: 嵌入式linux之go语言开发(三)卡库的封装

卡库的封装和调用,这是一个重头戏,完成了它,则就完整了所有的封装。至于网络通信,记录存储等,则可以使用go本身的模块去做。后续做一版完整的go语言版B503应用。


截至目前,非接触式卡库的封装接近尾声,这部分花了不少精力。


package drivers
/*
#cgo CFLAGS: -Iinclude
#cgo LDFLAGS: -Llib -lpicc
#include <stdlib.h>
#include "pcd_apis.h"
*/
import "C"
import "unsafe"
import (
  "fmt"
  "log"
)
const (
  DEF_PCD_SeleTypeA int = 1
  DEF_PCD_SeleTypeB int = 2
)
type __Pcd14443Cfg struct {
  PPS   int //AB  0:标准的支持PPS,非0:强制不支持PPS
  M1CPU int //AB  0:自动识别,1:强制M1,2:强制CPU
}
type __Pcd14443Info struct {
  ATQA []byte //A专用   REQA命令返回的ATQA值      固定有效2字节
  UID  []byte //A专用   卡片的UID,         长度为4,7,10字节
  UID_Size byte //A专用   卡片的UID长度          长度为4,7,10字节
  SAK      byte //A专用   卡片选卡成功返回的SAK值     固定有效1字节
  TypeAB   byte //A/B共用 当前是A卡还是B卡     DEF_PCD_SeleTypeA=A卡,DEF_PCD_SeleTypeB=B卡
  ATS_Size byte //A专用   接收到的ATS数据长度
  BActive  byte //B激活状态 0:非激活  非0:激活
  Rvs08bit byte
  Rvs16bit byte
  ATS []byte //A专用   ATS接收数据缓冲区 按中国金融规定 PICC回的数据最长为21字节
  ATQB []byte //B专用   卡片应答数据
} //14443应用数据结构
var (
  Pcd14443CfgMode int = DEF_PCD_SeleTypeA //PCD选择TypeA卡 操作PICC类型定义
  Pcd14443CfgA    __Pcd14443Cfg
  Pcd14443CfgB    __Pcd14443Cfg
  Pcd14443Info    __Pcd14443Info //14443协议层数据缓冲区
  PiccCid int
)
func init() {
  Pcd14443Info.ATQA = make([]byte, 4)
  Pcd14443Info.UID = make([]byte, 16)
  Pcd14443Info.ATS = make([]byte, 40)
  Pcd14443Info.ATQB = make([]byte, 20)
}
func ICC_PCD_SysCfg(mode, m1, pps int) {
  Pcd14443CfgMode = mode
  if DEF_PCD_SeleTypeA == Pcd14443CfgMode {
    Pcd14443CfgA.PPS = pps
    Pcd14443CfgA.M1CPU = m1
  } else if DEF_PCD_SeleTypeB == Pcd14443CfgMode {
    Pcd14443CfgB.PPS = pps
    Pcd14443CfgB.M1CPU = m1
  }
}
func ICC_PCD_Init() int {
  tpe := make([]byte, 4)
  para := make([]byte, 50)
  //var cardType *C.uchar = (*C.uchar)(unsafe.Pointer(&tpe[0]))
  //var rfPara *C.uchar = (*C.uchar)(unsafe.Pointer(&para[0]))
  cardType := (*C.uchar)(unsafe.Pointer(&tpe[0]))
  rfPara := (*C.uchar)(unsafe.Pointer(&para[0]))
  ret := C.PcdInit(cardType, rfPara)
  if ret != 0 {
    return 1
  }
  fmt.Println(tpe)
  fmt.Println(para)
  ret = C.PiccOpen()
  return int(ret)
}
func ICC_PCD_Open() int {
  ret := C.PiccOpen()
  return int(ret)
}
func ICC_PCD_Close() {
  C.PiccClose()
}
//ISO14443 A/B 使卡进入HALT状态
func ICC_PCD_Halt() int {
  //nc_iso14443_debug("%s","ICC_PCD_Halt\n");
  ret := C.PiccRemove('H', C.uchar(PiccCid))
  return int(ret)
}
//ISO14443 A/B 使已经进入HALT状态的卡激活,并且进行冲突循环,选卡操作
func ICC_PCD_WakeUp() int {
  ret := ICC_PCD_Request(Pcd14443CfgMode)
  return int(ret)
}
func ICC_PCD_Request(mode int) int {
  tpe := make([]byte, 4)
  sno := make([]byte, 100)
  oth := make([]byte, 100)
  pid := make([]byte, 1)
  lenth := 0
  ptr := 0
  ret := C.uchar(0)
  cardType := (*C.uchar)(unsafe.Pointer(&tpe[0]))
  serialNo := (*C.uchar)(unsafe.Pointer(&sno[0]))
  other := (*C.uchar)(unsafe.Pointer(&oth[0]))
  piccid := (*C.uchar)(unsafe.Pointer(&pid[0]))
  if DEF_PCD_SeleTypeA == Pcd14443CfgMode {
    if DEF_PCD_SeleTypeA == Pcd14443CfgA.M1CPU { //强制M1
      ret = C.PiccDetect('M', cardType, serialNo, piccid, other)
    } else if DEF_PCD_SeleTypeB == Pcd14443CfgA.M1CPU { //强制CPUA
      ret = C.PiccDetect('A', cardType, serialNo, piccid, other)
    } else if 3 == Pcd14443CfgA.M1CPU { //自动检测A,B,无法检测到纯M1 操作一次28ms
      ret = C.PiccDetect(0x01, cardType, serialNo, piccid, other)
    } else { //自动CPUA/M1 操作一次18ms
      ret = C.PiccDetect('X', cardType, serialNo, piccid, other)
    }
    //    printf(">>>>>>>>>>>>PiccDetect, ret=%d   count=%d\n", ret, count++);
  } else if DEF_PCD_SeleTypeB == Pcd14443CfgMode { //强制CPUB
    ret = C.PiccDetect('B', cardType, serialNo, piccid, other)
  } else {
    log.Fatal("err config!")
  }
  //fmt.Printf("ret = %d\n", int(ret))
  PiccCid = int(pid[0])
  if ret == 0 {
    //fmt.Printf("tpe:%x\n", tpe)
    //fmt.Printf("sno:%x\n", sno)
    //fmt.Printf("oth:%x\n", oth)
    if 'B' == tpe[0] {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeB) //TYPE B
    } else if 'M' == tpe[0] {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeA) //TYPE A
    } else {
      Pcd14443Info.TypeAB = byte(DEF_PCD_SeleTypeA) //TYPE A
    }
    if sno[0] > 10 { //序列号长度不能大于10
      return 1
    }
    Pcd14443Info.UID_Size = sno[0]
    copy(Pcd14443Info.UID, sno[1:1+Pcd14443Info.UID_Size])
    if oth[0] > 2 {
      ptr = 3
      lenth = int(oth[ptr])
      ptr += 1
      if ptr+lenth < len(oth) {
        copy(Pcd14443Info.ATQA, oth[ptr:ptr+2])
        //fmt.Printf("ATQA:%x\n", Pcd14443Info.ATQA)
      }
      ptr += lenth
      lenth = int(oth[ptr])
      ptr += 1
      if ptr+lenth < len(oth) {
        Pcd14443Info.SAK = oth[ptr]
      }
      ptr += lenth
      lenth = int(oth[ptr])
      if 'A' == tpe[0] && (ptr+lenth < len(oth)) {
        copy(Pcd14443Info.ATS, oth[ptr:ptr+lenth])
        Pcd14443Info.ATS_Size = byte(lenth)
      }
      //fmt.Println(Pcd14443Info)
      fmt.Printf("ATQA:%x\n", Pcd14443Info.ATQA)
      fmt.Printf("SAK:%x\n", Pcd14443Info.SAK)
      fmt.Printf("UID:%x\n", Pcd14443Info.UID)
    }
  } else {
    Pcd14443Info.ATQA[0] = 0
    Pcd14443Info.ATQA[1] = 0
    Pcd14443Info.ATQA[2] = 0
    Pcd14443Info.ATQA[3] = 0
  }
  return int(ret)
}
//ISO14443 A/B 检测卡片是否存在,卡片激活状态后调用
func ICC_PCD_CheckPICCRounge() int {
  ret := C.PiccRemove('C', C.uchar(PiccCid))
  //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
  if 0x06 == ret { //卡片仍在感应区
    //nc_iso14443_debug( "%s", "Card exist\n" );  //PiccRemove('C')停活卡片,需要重新寻卡以激活卡片
    return 0 //返回卡片仍在
  } else {
    //nc_iso14443_debug( "%s", "Card removed\n" );
    return 1 //返回卡片离开
  }
}
//ISO14443 A/B 检测PICC是否移出工作场
func ICC_PCD_CheckPICCrf() int {
  ret := C.PiccRemove('R', C.uchar(PiccCid))
  //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
  if 0x06 == ret { //卡片仍在感应区
    //nc_iso14443_debug( "%s", "Card exist\n" );  //PiccRemove('C')停活卡片,需要重新寻卡以激活卡片
    return 0 //返回卡片仍在
  } else {
    //nc_iso14443_debug( "%s", "Card removed\n" );
    return 1 //返回卡片离开
  }
}
//ISO14443 A/B 等待PICC移出工作场
func ICC_PCD_WaitPICCrf() int {
  for true {
    ret := C.PiccRemove('C', C.uchar(PiccCid))
    //nc_iso14443_debug("PiccRemove('C').ret=%d\n", ret);
    if 0x06 == ret {
      //nc_iso14443_debug( "%s", "Card exist\n" );
      continue //返回卡片仍在
    } else {
      //nc_iso14443_debug( "%s", "Card removed\n" );
      break //卡片移出
    }
  }
  return 0
}
//ISO14443 A/B 复位工作场
func ICC_PCD_ResetPCDrf() int {
  C.PiccClose()
  ret := C.PiccOpen()
  //nc_iso14443_debug("ICC_PCD_ResetPCDrf. ret = %d\n", ret);
  return int(ret)
}
func ICC_PCD_APDUCommand(in []byte, inlen int, out []byte, outlen *int, maxsize int, lc, le byte) int {
  var ApduSend C.APDU_SEND
  var ApduResp C.APDU_RESP
  ApduSend.Command[0] = C.uchar(in[0])
  ApduSend.Command[1] = C.uchar(in[1])
  ApduSend.Command[2] = C.uchar(in[2])
  ApduSend.Command[3] = C.uchar(in[3])
  ApduSend.Lc = C.ushort(lc)
  for i := 0; i < int(lc); i++ {
    ApduSend.DataIn[i] = C.uchar(in[5+i])
  }
  if le != 0 { //此处须填非0值,若le非0则填实际值,否则固定填256
    ApduSend.Le = C.ushort(le)
  } else {
    ApduSend.Le = 256
  }
  fmt.Printf("->APDU:%x\n", in[0:inlen])
  ret := C.PiccIsoCommand(C.uchar(PiccCid), &ApduSend, &ApduResp)
  if ret != 0 {
    return int(ret)
  }
  if (le != 0) && (byte(ApduResp.LenOut) != le) {
  }
  if int(ApduResp.LenOut+2) < maxsize { //还有两个字节的状态字节 SWA/SWB
    //memcpy( out, &ApduResp.DataOut[0], ApduResp.LenOut );
    for i := 0; i < int(ApduResp.LenOut); i++ {
      out[i] = byte(ApduResp.DataOut[i])
    }
    out[ApduResp.LenOut] = byte(ApduResp.SWA)
    out[ApduResp.LenOut+1] = byte(ApduResp.SWB)
  } else {
    //memcpy( out, &ApduResp.DataOut[0], maxsize-2 );
    for i := 0; i < maxsize-2; i++ {
      out[i] = byte(ApduResp.DataOut[i])
    }
    out[ApduResp.LenOut] = byte(ApduResp.SWA)
    out[ApduResp.LenOut+1] = byte(ApduResp.SWB)
  }
  *outlen = int(ApduResp.LenOut + 2)
  fmt.Printf("<-APDU:%x\n", out[0:ApduResp.LenOut+2])
  return 0
}
func main() {
  fmt.Println("Hello Go")
  ret := ICC_PCD_Init()
  if ret == 0 {
    fmt.Println("ICC PCD init ok!")
    ICC_PCD_SysCfg(DEF_PCD_SeleTypeA, 0, 1)
    for i := 0; i < 100; i++ {
      ret = ICC_PCD_Request(DEF_PCD_SeleTypeA)
      if ret == 0 {
        fmt.Println("find card ok!")
      } else {
        fmt.Println("not find card!")
      }
    }
  } else {
    fmt.Printf("ICC PCD init err!,code=%d\n", ret)
  }
  name := ""
  fmt.Println("over!press any key to continue: ")
  fmt.Scanln(&name)
}


package card
import (
  "encoding/hex"
  "fmt"
  "go8583/drivers"
  "math/rand"
  "time"
)
func ICF_GetChallenge8B(Rnd []byte, ich int) int {
  CmdBuffer[0] = 0x00 //CLA
  CmdBuffer[1] = 0x84 //INS
  CmdBuffer[2] = 0x00 //P1
  CmdBuffer[3] = 0x00 //P2
  CmdBuffer[4] = 0x08 //Le
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, 5, RcvBuffer, &Grcv_Len, 260, 0, CmdBuffer[4])
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  copy(Rnd, RcvBuffer[0:8])
  return rcode
}
func ICF_SelectAID(AID []byte, ilen int, ich int) int {
  CmdBuffer[0] = 0x00       //CLA
  CmdBuffer[1] = 0xA4       //INS
  CmdBuffer[2] = 0x04       //P1
  CmdBuffer[3] = 0x00       //P2
  CmdBuffer[4] = byte(ilen) //Lc
  copy(CmdBuffer[5:], AID[0:ilen])
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, ilen+5, RcvBuffer, &Grcv_Len, 260, CmdBuffer[4], 0)
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  return rcode
}
//{9f38 18 9f66049f02069f03069f1a0295055f2a029a039c019f3704}
func UP_GPO(pdoc []byte, lenth byte, ich int) int {
  CmdBuffer[0] = 0x80      //CLA
  CmdBuffer[1] = 0xA8      //INS
  CmdBuffer[2] = 0x00      //P1
  CmdBuffer[3] = 0x00      //P2 ‘01’用于ED(电子存折,需要个人密码PIN ‘02’用于EP(电子钱包)
  CmdBuffer[4] = lenth + 2 //Lc
  CmdBuffer[5] = 0x83
  CmdBuffer[6] = lenth
  copy(CmdBuffer[7:], pdoc[0:lenth])
  rcode := drivers.ICC_APDU_Exchange(ich, CmdBuffer, int(lenth+8), RcvBuffer, &Grcv_Len, 260, CmdBuffer[4], 0)
  if rcode != 0 {
    fmt.Printf("ICC_APDU_Exchange err,code=%d\n", rcode)
    return rcode
  }
  if Grcv_Len < 2 {
    return 2
  }
  rcode = ((int(RcvBuffer[Grcv_Len-2]) << 8) | int(RcvBuffer[Grcv_Len-1]))
  if rcode != 0x9000 {
    return rcode
  }
  return rcode
}
/*
*双免GPO组包
 */
//{9f38 18 9f66049f02069f03069f1a0295055f2a029a039c019f3704}
func UP_qUICS(money int, opdt string, ich int) int {
  t9F66 := "........"                  //交易属性
  t9F02 := fmt.Sprintf("%012d", money) //授权金额
  t9F03 := "000000000000"
  t9F1A := "0156"
  t95 := "0000000000"
  t5F2A := "0156"
  t9A := opdt
  t9C := "00"
  rand.Seed(time.Now().Unix())
  t9F37 := fmt.Sprintf("%08x", rand.Int31())
  pdoc := t9F66 + t9F02 + t9F03 + t9F1A + t95 + t5F2A + t9A + t9C + t9F37
  fmt.Printf("pdoc:%s\n", pdoc)
  bpdoc, err := hex.DecodeString(pdoc)
  if err != nil {
    fmt.Printf("DecodeString error:%s\n", err.Error())
  }
  fmt.Printf("bpdoc:%x\n", bpdoc)
  return UP_GPO(bpdoc, byte(len(bpdoc)), ich)
}


root@b503_lcd:/app/city_app/opt# ./cardlib
Hello Go
ATQA:08000000
SAK:20
UID:5deaa62a000000000000000000000000
find card ok!
ATQA:04000000
SAK:28
UID:2f7bb136000000000000000000000000
find card ok!
->APDU:00a404000e325041592e5359532e4444463031
<-APDU:6f30840e325041592e5359532e4444463031a51ebf0c1b61194f08a000000333010101500a50424f432044454249548701019000
ICF_SelectAID ok!
[{6f 30 840e325041592e5359532e4444463031a51ebf0c1b61194f08a000000333010101500a50424f43204445424954870101} {84 0e 325041592e5359532e4444463031} {a5 1e bf0c1b61194f08a000000333010101500a50424f43204445424954870101} {bf0c 1b 61194f08a000000333010101500a50424f43204445424954870101} {61 19 4f08a000000333010101500a50424f43204445424954870101} {4f 08 a000000333010101} {50 0a 50424f43204445424954} {87 01 01}]


相关文章
|
6月前
|
网络协议 算法 Linux
【嵌入式软件工程师面经】Linux网络编程Socket
【嵌入式软件工程师面经】Linux网络编程Socket
173 1
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
107 3
|
4月前
|
传感器 人工智能 网络协议
:嵌入式 Linux 及其用途
【8月更文挑战第24天】
196 0
|
5月前
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
**嵌入式Linux学习误区摘要** 1. **过度聚焦桌面Linux** - 许多学习者误将大量时间用于精通桌面Linux系统(如RedHat、Fedora、Ubuntu),认为这是嵌入式Linux开发的基石。 - 实际上,桌面Linux仅作为开发工具和环境,目标不应是成为Linux服务器专家,而应专注于嵌入式开发工具和流程。 2. **盲目阅读Linux内核源码** - 初学者在不了解Linux基本知识时试图直接研读内核源码,这往往导致困惑和挫败感。 - 在具备一定嵌入式Linux开发经验后再有针对性地阅读源码,才能有效提升技能。
|
5月前
|
Linux Go API
go创建web项目分别在windows和linux部署
go创建web项目分别在windows和linux部署
59 0
|
17天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
108 6
|
18天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
58 3
|
18天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
50 2
|
25天前
|
缓存 监控 Linux