腾讯车联卡通过ICCID查询流量

#!/usr/bin/python3

import time
import hashlib
import json
import base64
import hmac

import requests
import jwt



import sys
if len(sys.argv) != 2:
    print("i need one ICCID.")
    exit(-1)


iccid = sys.argv[1]




private_key = b"""-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCtkpIspKwGBKe/
qSrx/JwOf/p/phAP132Tuy2XSSmas1hx49EMRZg6meEkPwz2utYQX2NKFi/CrYd3
fOrFa4HguokD9A5mdlX4TPxea91fOnFOOTdpS3iYYAYQpbR3lsE5Xau/jCcExaXz
MEgG5XVRxpmFcqwvC5LYvbt37XjxvlZE2VpY8sEItOl9ALyYT3Phxyt7bHz1Vhtk
/C5mtADf7OW9eUaORIVoKfFss4vsRf3MS7oi81II+izxde0StR+Psr6htlKerylh
4FffN6buyCooKk7Lj4Y2uvy7eMsWgjanYOZdZ17+2nojDq3ev5BuD/yGrzX1NYFO
m4JhZd2FAgMBAAECggEANpW7hnKa7qGUJ4AJ02P/p8ezfWU58+FuyoC4y7TkcpQo
IljkqxlP0E4IMZ/qccU8AJYe4XOsvWpZCcitBDzU8Vl4MVgVFz/rtVRwoTkGuyXj
8BeqR2DGohTAk4epA1rTlTjSBEENHEET+TCb1h9fD8KHESKI55qaKbMxO3vg5xTR
5cCb+Zp6vu/tZw/LeYY3aNocD9Jy7WHDLk7HgT9QQ58LI7zOAhfVj+eVnhR8m/me
3tz2LWCOxzKi5QpCP8dAUxED17C0sl+8uOup/pDvcz9s9FBmTNeljtSV6xlJ8Xhr
V9LvzRrPmERc4bVikmomeS7BAAkOcVeUUQixNB5SmQKBgQDY0CYPAGZA8ST7HVCb
5eMTpxozczQTOjK0z9PETgXsepLcIcmVtAQU7NM6rkigOp1ocHOP0JGyUWNuyWhc
zeKy/5mcWdxXPnJaiYoWY5p9WfYWALkgTe1FyFwsqMPS9m4BX4vxE6htvEZ315mE
uQMS35Fqv8UqhZdHelu14lwfqwKBgQDM8bNY7pc6V5KPaghJUpXmkCn9JoWKANZV
N41zUAS8V9iZgovrys0/988uHYNB8NRlDy7gfHrcm9KJfqnuDE/aA5+E/siQCzJp
CHn+FTPOVVATj6OHgppYy6bFizoPt1B9hN2utebUmn7g9JsKsXcepHbwyf51iFX9
G1w3KLeHjwKBgA5v7djZ4fU5GYlPLmrzJ+fLHMeISFNvcKjxndxYhbR1hQn7hfzd
+9eY3Yfuyw9AV/L4afmNneYN3V0nNDfZzdq1gnkS0sVMdzjoJ7XN9B+cufJFIg6w
obgf22Nt6XxGxJR61Eq9UF3eR6L7TFG2MCo1kO1Wz75BHw23bBv0csX5AoGAdFuy
QQbNk9FOo+BGriKWex5UyKcLYIwTJLTyuYqv9aNGlfm5qXkuSWhQF9WnxVOPs4UI
T2o9c22A/pGMt1wEJjqyYhcqFiZVS68FXE37aOWlHkYKSZDMQyf1GuluYKfqqik6
l8wtL348FUqrkEmr4Rjtcs6si1yJvbfABo02jl8CgYEApDr46GEcAV0dfr3AvhS7
t9DNYCdXiXa8RLcSKiNO6Iimwav7hn+IuN2WKJ/uPXqCSFAwVSYhRyiLqWQ6QSRy
vU4q1zVDn+SxxY79dWWsSrRlAuCmyVmD5mJdQlmY3FC8KXj14mEsG+eZtmHX7HTU
UaKPdheWKWAOQeqtRRNEZew=
-----END PRIVATE KEY-----
"""

publick_key = b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArZKSLKSsBgSnv6kq8fyc
Dn/6f6YQD9d9k7stl0kpmrNYcePRDEWYOpnhJD8M9rrWEF9jShYvwq2Hd3zqxWuB
4LqJA/QOZnZV+Ez8XmvdXzpxTjk3aUt4mGAGEKW0d5bBOV2rv4wnBMWl8zBIBuV1
UcaZhXKsLwuS2L27d+148b5WRNlaWPLBCLTpfQC8mE9z4ccre2x89VYbZPwuZrQA
3+zlvXlGjkSFaCnxbLOL7EX9zEu6IvNSCPos8XXtErUfj7K+obZSnq8pYeBX3zem
7sgqKCpOy4+GNrr8u3jLFoI2p2DmXWde/tp6Iw6t3r+Qbg/8hq819TWBTpuCYWXd
hQIDAQAB
-----END PUBLIC KEY-----
"""


ts = int(time.time())


headers = {
    "typ": "JWT",
    "kid": "tsmkc-client",
    "alg": "RS256"
}

claims = {
	"sub":    "kingcard",
	"iss":    "tsmkc.qq.com",
	"exp":    ts + 300,
	"iat":    ts - 10,
	"iccid":  iccid,
	"userid":  "",
	"usersig": ""
}

encoded_token = jwt.encode(claims,  private_key,   algorithm="RS256",   headers=headers)

get_headers = {
	'Content-Type': 'application/json',
	'user-agent': 'okhttp/3.5.0',
	'Authorization': 'Bearer ' + encoded_token.decode("ascii")
}

r = requests.get('https://api.tsmkc.qq.com/prd/v1/package/packageinfo', headers=get_headers)
print(r.text)

用的jwt做的认证

用usim卡接入ims

USIM可接入2G/3G/4G/5G网络,但不存储IMS网络相关的用户信息.接入VoLTE网络时, 需要终端 通过某种规则,生成ims系统的用户码号和归属地等信息。 或者直接从ISIM读取相关信息:
1. 私有标识(IMPI)
归属网络提供的用户全球唯一标识,用于IMS网络对IMS用户进行鉴权认证
EF_IMPI (IMS private user identity)
文件标识符:6F02
SFI: 02
内容格式为 NAI TLV, tag(type) 值为 0x80

建议的生成规则(以中国移动为例子):
IMSI@ims.mnc00.mcc46.3gppnetwork.org

2. EF_DOMAIN (Home Network Domain Name)
6F03
SFI: 05
归属地运营商的网络域名
tag也为80

3. EF_IMPU (IMS public user identity)
6F04
SFI: 04
本文件可以含有一个或者多个记录。每个记录是一个公开的 SIP Identity (SIP URI)

用于用户之间进行通信的标识,对用户可见。IMPU采用SIP URI 和 TEL URI 格式。
SIP URI的例子: sip:user@chinamobile.com:port
sip:10086@bj.imsi.mnc00.mcc460.3gppnetwork.org
TEL URI的例子: tel:+8610086

———————————
对于没有ISIM aid的USIM卡,可以通过IMSI来生成自己的IMPI和IMPU:
IMPI: imsi@ims.mnc00.mcc460.3gppnetwork.org
IMPU: sip:imsi@ims.mnc00.mcc460.3gppnetwork.org

临时IMPU仅仅用于注册请求。如果成功注册后,UE 将获得相应的公有用户标识,UE 将在后续非注册请求中用任意一个公有用户标识。
在注册时,携带导出的IMPI和IMPU,注册成功后,网络根据用户签约返回两个IMPU值,分别为Tel URI 和 SIP URI形式。
Tel URI用于语音呼叫,SIP URI用于IMS网络路由。

ISO 14443 非接触式IC卡

PICC : Proximity Integrated Circuit Card 卡
PCD: Proximity Coupling Device 接近式耦合设备,通常指读卡器

ReqA: Request command, Type A
ReqB: Request command, Type A, 在 NFC中,叫 SENSB_REQ
ATQA: Answer To Request, Type A, 在NFC标准中,叫 SENS_RES
ATQB: Answer To Request, Type B, 在NFC标准,这个叫 SENSB_RES
ATTRIB: PICC 选择命令,TypeB
WUPA: Wake-Up Command, Type A
WUPB: Wake-Up Command, Type B, 在NFC中,叫 ALLB_REQ, 轮询命令
ATS: Answer to Select
ATR: Answer to Reset
PTS: Protocol Type Selection
PPS: Protocol and Parameter Selectrion
RATS: Request for Answer to Select

——————————————————
当 PICC 暴露于未调制的工作场内,它能在5ms 内接受一个请求。
例如:
当类型A PICC 接收到任何类型B 命令时,它能在5ms 内接受一个REQA。
当类型B PICC 接收到任何类型A 命令时,它能在5ms 内接受一个REQB。

为了检测到是否有PICCs进入到PCD的有效作用区域,PCD重复的发出请求信号REQ,并判断是否有响应ATQ。请求信号必须是REQA和REQB,附加ISO/IEC14443其它部分的描述的代码。A型卡和B型卡的命令和响应不能够相互干扰。
———————————————-
当一个B型卡被置入阅读器的作用范围内,IC卡执行一些预置程序后进入”闲置状态”,等待接收有效的REQB命令。对于B型卡,通过发送REQB命令,可以直接启动SlottedALOHA防碰撞算法,选出一张卡,对其进行操作。

当接收到有效的REQB帧的命令,PICC定义了单一的时间槽用来发送ATQB。
如果是PICC定义的第一个时间槽,PICC必须发送ATQB的响应信号,然后进入准备—已声明子状态。
如果不是PICC定义的第一个时间槽,PICC进入准备—已请求子状态。
———————
在已声明子状态下,PICC已经上电,并且已经发送了对REQB的ATQB响应。
它监听REQB和ATTRIB的数据帧。
————
激活状态
PICC已经上电,并且通过ATTRIB命令的前缀分配到了通道号,进入到应用模式。
————————-
frameworks / base / core / java / android / nfc / tech / NfcB.java

public byte[] getApplicationData()
在Tag发现阶段,从 ATQB/SENSB_RES 中得到的应用数据

public byte[] getProtocolInfo()
在Tag发现阶段,从 ATQB/SENSB_RES 中得到的协议信息

public byte[] transceive(byte[] data)
发送原生的nfcb命令给tag, 并收到响应
1)应用 不必要添加 EoD (CRC) 到payload, 校验会被自动计算。
2)应用也不应该发送那些管理 轮询和初始化的命令 :SENSB_REQ, SLOT_MARKER 等
——————————-
frameworks/base/core/java/android/nfc/Tag.java
public byte[] getPollBytes() {
在轮询时间,Tag返回的底层字节
这些字节的意义,依赖于所采用的Tag技术
在ISO14443-3A , 叫做 ATQA/SENS_RES
在ISO14443-3B, 包含 来自于 ATQB/SENSB_RES 的 Application data (4 bytes) 和 Protocol Info (3 bytes)

public byte[] getActivationBytes() {
在激活阶段, Tag返回的底层字节
ISO14443-3B & ISO14443-4中,叫 ATTRIB response
ISO14443-3A & ISO14443-4 叫做 SAK/SEL_RES, historical bytes from ATS

Quectel RM500U-CN

移远通信RM500U-CN 是一款专为 IoT/eMBB 应用而设计的 5G Sub-6 GHz 模块。采用 3GPP Release 15 技术,同时支持 5G NSA 和 SA 模式。RM500U-CN 采用 M.2 封装,与移远通信RM500Q系列模块兼容。

RM500U-CN 支持国内四大运营商。

RM500U-CN为工规级模块,仅适用于工业级和商业级应用。

RM500U-CN 内置丰富的网络协议,集成多个工业标准接口,并支持多种驱动和软件功能(如 Windows、Linux、Android 等操作系统下的 USB/PCIe 驱动等)

支持频段: n1/n28(广电700M)/n41/n77/n78/n79

cat /sys/kernel/debug/usb/devices

T: Bus=02 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
D: Ver= 3.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1
P: Vendor=2c7c ProdID=0900 Rev= 4.04
S: Manufacturer=Quectel
S: Product=RM500U-CN
S: SerialNumber=0123456789ABCDEF
C:* #Ifs= 7 Cfg#= 1 Atr=c0 MxPwr=504mA
A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03
I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host
E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E: Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E: Ad=85(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E: Ad=87(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=06(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms


# modprobe  -v option
insmod /lib/modules/5.10.0-8-amd64/kernel/drivers/usb/serial/usbserial.ko
insmod /lib/modules/5.10.0-8-amd64/kernel/drivers/usb/serial/usb_wwan.ko
insmod /lib/modules/5.10.0-8-amd64/kernel/drivers/usb/serial/option.ko
# echo '2c7c 0900' > /sys/bus/usb-serial/drivers/option1/new_id

就会正确加载驱动了
T: Bus=02 Lev=01 Prnt=01 Port=04 Cnt=01 Dev#= 3 Spd=5000 MxCh= 0
D: Ver= 3.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1
P: Vendor=2c7c ProdID=0900 Rev= 4.04
S: Manufacturer=Quectel
S: Product=RM500U-CN
S: SerialNumber=0123456789ABCDEF
C:* #Ifs= 7 Cfg#= 1 Atr=c0 MxPwr=504mA
A: FirstIf#= 0 IfCount= 2 Cls=e0(wlcon) Sub=01 Prot=03
I:* If#= 0 Alt= 0 #EPs= 1 Cls=e0(wlcon) Sub=01 Prot=03 Driver=rndis_host
E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=rndis_host
E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=01(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=83(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=84(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=85(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=04(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=86(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
I:* If#= 6 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option
E: Ad=87(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms
E: Ad=06(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms
在 /dev/下会出现多个usb串口,第3个是AT命令端口

picocom -b 23400 -c /dev/ttyUSB2
at+cgmr
Platform Version: MOCORTM_20C_W21.17.3_P2_Debug
Project Version:   Orca_NR_modem
BASE  Version:    5G_MODEM_20C_W21.17.3_P2

用lsusb -t 以树形显示 挂载关系和速率

# lsusb -t
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/8p, 5000M
    |__ Port 1: Dev 2, If 0, Class=Wireless, Driver=rndis_host, 5000M
    |__ Port 1: Dev 2, If 1, Class=CDC Data, Driver=rndis_host, 5000M
    |__ Port 1: Dev 2, If 2, Class=Vendor Specific Class, Driver=, 5000M
    |__ Port 1: Dev 2, If 3, Class=Vendor Specific Class, Driver=, 5000M
    |__ Port 1: Dev 2, If 4, Class=Vendor Specific Class, Driver=, 5000M
    |__ Port 1: Dev 2, If 5, Class=Vendor Specific Class, Driver=, 5000M
    |__ Port 1: Dev 2, If 6, Class=Vendor Specific Class, Driver=, 5000M

可以看到 2,3,4,5,6 功能没有加载正确的驱动

ls -la /sys/bus/usb/devices/
...
lrwxrwxrwx 1 root root 0  9月 22 00:42 2-1:1.2 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.2
lrwxrwxrwx 1 root root 0  9月 22 00:42 2-1:1.3 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.3
lrwxrwxrwx 1 root root 0  9月 22 00:42 2-1:1.4 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.4
lrwxrwxrwx 1 root root 0  9月 22 00:42 2-1:1.5 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.5
lrwxrwxrwx 1 root root 0  9月 22 00:42 2-1:1.6 -> ../../../devices/pci0000:00/0000:00:14.0/usb2/2-1/2-1:1.6
...

手动绑定

ls -la /sys/bus/usb/drivers/rndis_host/
echo '2-1:1.0'  >   /sys/bus/usb/drivers/rndis_host/bind
echo '2-1:1.1'  >   /sys/bus/usb/drivers/rndis_host/bind

查询/设置当前usb网卡驱动模型

AT+QCFG="usbnet"[,net]

1 表示 ECM
2 表示 MBIM
3 表示 RNDIS
5 表示 NCM
默认为 3, 因为rndis在Linux和Windows下都有驱动

配置网卡模式

AT+QCFG="nat"[,nat]
0=网卡模式
1=路由模式
2=网桥模式
默认为0,网卡模式。网卡模式下,分配给PC机的地址为公网地址(也就是核心网分配给基带的地址)。此时modem内部无法连接外网,因为它都没有IP了,IP给了PC机


网桥模式和路由模式为 内网地址

AT+QCFG=”ethernet” (通过 pcie外接了以太网网卡芯片,这个指令才有用)

配置USB

AT+QCFG="usbcfg"[, vid, pid, diag,log,at_port,modem,NMEA,ADB]
示例:
AT+QCFG="usbcfg",0x2c7c,0x0900,1,1,1,1,1,1
打开全部功能,包括adb
默认情况下,adb是关闭的
还可以用AT+QCFG="usbid"[,vid,pid] 来单独配置vid和pid

启用ims/volte

AT+QCFG="ims",1

配置网络搜索模式

AT+QNWPREFCFG="mode_pref"

配置通话语音

AT+QNWPREFCFG="voice_domain"

配置语音优先还是数据优先

AT+QNWPREFCFG="ue_usage_setting"

查询5G注册状态

AT+C5GREG

+C5GREG: 0,1

查询5G网络质量

AT+C5GQOS?
+C5GQOS: 1,0,0,0,0,0
+C5GQOS: 2,0,0,0,0,0
+C5GQOS: 11,0,0,0,0,0


AT+C5GQOSRDP=1

Cryptodome.PublicKey导入RSA private key

Crypto.PublicKey.RSA.import_key(extern_key, passphrase=None)
作用:导入RSA key(公钥或者私钥)
extern_key为 string或者 byte string
支持PKCS#1 的 DER格式(二进制或者PEM编码)
私钥,还支持 PKCS#8 格式 (相当于PKCS#1 还有一些额外信息)


Crypto.PublicKey.RSA.construct(rsa_components, consistency_check=True)
作用:用大整数 构造 私钥 或者 公钥
modulus (n) 必选参数
public exponent (e) 必选参数
private exponent (d) key的类型为私钥时,必选
p
q

rsa_private_key = RSA.construct((modulus, pubExp, privateExp, prime1, prime2))


NfcB读取身份证UID

先发送05 00 00询卡命令后,身份证返回12字节卡类型代码,不同身份证返回的数据不一样。比如对两个身份证发送询卡命令后分别返回
50 00 00 00 00 d1 03 86 0c 00 80 80

50 00 00 00 00 d1 03 86 07 00 80 90

询卡成功后,发送SELECT命令:1d 00 00 00 00 00 08 01 08 对二代身份证进行选卡操作。
这时身份证返回的第一个字节应该等于SELECT命令的最后一个字节08,根据这个条件来判断SELECT命令是否操作成功。

最后发送GUID命令:00 36 00 00 08,身份证会返回10字节数据,其中前8字节就是身份证的唯一ID,后两字节是90 00(SW1 SW2)。
=================================
二代证是应答标准的REQB指令的,但Attrib指令是非标的,
二代证的唯一UID可以通过下面的步骤简单读取,
1. PCD发送REQB命令: 05 00 00 二代证收到后会发送ATQB(12 bytes)应答。
例:50 00 00 00 00 d1 03 86 0c 00 80 80
2.PCD发送非标Attrib命令
1d 00 00 00 00 00 08 01 08
二代证返回应答:08 //这里返回08不能认为是错误的,ISO14443标准返回为00

3.PCD发送读取UID命令
00 36 00 00 08 二代证返回应答:xx xx xx xx xx xx xx xx 90 00

Sent bits: 05 00 00
Received bits: 50 00 00 00 00 d1 03 86 0c 00 80 80
Sent bits: 1d 00 00 00 00 00 08 01 08
Received bits: 08
Sent bits: 00 36 00 00 08
Received bits: xx xx xx xx xx xx xx xx 90 00
===========================
读二代证的 寄存器配置信息
WriteRegPN532(CIU_MODE, 0xFF);
WriteRegPN532(CIU_TXAUTO, 0x00);
WriteRegPN532(CIU_TXMODE, 0x03);
WriteRegPN532(CIU_RXMODE, 0x03);
WriteRegPN532(CIU_TYPEB, 0x03);
WriteRegPN532(CIU_DEMOD, 0x4D);
WriteRegPN532(CIU_GSNON, 0xFF);
WriteRegPN532(CIU_CWGSP, 0x3F);
WriteRegPN532(CIU_MODGSP, 0x18);
WriteRegPN532(CIU_RXTHRESHOLD, 0x4D);
WriteRegPN532(CIU_MODWIDTH, 0x68);
WriteRegPN532(CIU_MANUALRCV,0x10);

发送命令 后面2字节是CRC校验
05 00 00 71 ff
1d 00 00 00 00 00 08 01 08 f3 10
00 36 00 00 08 57 44
============================
由于二代证的ATTRIB操作非标,所以不能直接用PN532提供的InListPassiveTarget等上层指令来选卡,
只能靠自己设置寄存器,并通过InCommunicateThru底层通讯,发送ATQB和ATTRIB,完成选卡操作。
—————————-
二代证的ATQB信息里会指明使用了非标准协议,所以 通常的标准type B协议不改造可能就会拒读二代证(类似于目前的普通读写器,只有REQB和ATQB的交互,因为得知卡片后续采用专有协议,而读写器如果不支持,就会停止进一步交互或者重复发送REQB),所以CLF的代码必须要修改以支持读取二代证的。
非接触式前端(CLF)

身份证阅读器通信协议

数据输入格式
Preamble (5个字节,AA AA AA 96 69, 前3个字节用于 帧同步, 后面2个字节,表示帧开始)
数据长度 (2个字节,高位在前,低位在后, 数据长度是指 本帧之后的所有有效数据的长度,CMD+Para+Data+CHKSUM)
CMD (命令,1个字节)
Para (参数,1个字节)
Data (数据,n个字节)
CHK_SUM (校验和,1个字节, 数据帧中除帧头和校验和之外的数据逐字节按位异或的结果)


数据输出格式:
Preamble (5个字节,跟输入格式中的一样,也是 AA AA AA 96 69)
数据长度 (2个字节,高位在前,低位在后, 有效数据=SW1+SW2+SW3+Data+CHK_Sum)
SW1,SW2(各1字节, 阅读器转发证卡 回应的APDU中的SW1,SW2)
SW3 (1字节, 阅读器自身的状态码)

说明命令CMD参数Para
复位10FF
状态检测11FF
读SAM模块编号12FF
寻找身份证2001
选取身份证2002
读取基本信息3001
读取最新追加信息3003
设置串口速率115200 bps6000
设置串口速率57600bps6001
设置串口速率38400bps6002
设置串口速率19200bps6003
设置串口速率9600bps6004
命令集

状态

SW1 SW2 SW3Data说明
00 00 90和具体命令有关,可能为空操作成功
00 00 9F卡的IIN返回找卡成功信息
00 00 10接收业务终端数据的校验和错误
00 00 11接收业务终端数据的长度错误
00 00 21接收业务终端的命令错误,包括命令中的各种数值或者逻辑搭配错误
00 00 24无法识别的错误
XX XX 31身份证对阅读器认证失败
00 00 32阅读器对身份证认证失败
00 00 33信息验证错误
00 00 40错误的卡
XX XX 41读身份证失败
XX Xx 47取随机数失败
00 00 60阅读器自检失败,不能接收任何命令
00 00 66阅读器未经过授权,无法使用
00 00 80 搜寻身份证失败
XX XX 81选取身份证失败
00 00 91没有这项内容

读取基本信息的响应

aaaaaa9669 (Preamble)
0508 (长度)
00 00 90 (SW1 SW2 SW3)

data(长度为1284字节的数据, 1284+1+3=1288=0x508)

chksum

data的内容

0100 (文字信息的长度, 256字节)
0400 (照片的长度)

姓名 (30个字节), 也就是说姓名最长的为15个字,不足的补空格

性别(2个字节) 男=1, 女=0

民族 (4个字节)

出生日期(16个字节)

住址(70个字节,最多35个字,不足的补空格)

身份证号(36字节)

签发机关(30个字节,最多15个字,不足的补空格)

有效起始 (16个字节)

有效结束( 16个字节, 年满46周岁的,此处为汉字”长期”, 补空格)

长度36的备用字节, 用空格填充


校验和算法:

def calculate_xor_checksum(message):
    checksum = 0
    for e in message:
        checksum ^= e
    return checksum

TCP_NODELAY与包合并

python需要设置

setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

只有有数据,就会发送出去
不会等到凑到 最大传输单元再发

asyncio的socket选项 TCP_NODELAY 在python 3.6后就被默认设置

https://stackoverflow.com/questions/50790579/prevent-msg-concatenation-with-asyncio-sockets

并行任务concurrent.futures和Coroutines

import time
from concurrent.futures import ProcessPoolExecutor

def print_hello():
    while True:
        print('sleep 1')
        time.sleep(1) 


def print_goodbye():
    while True:
        print('sleep 2')
        time.sleep(2) 
        
with ProcessPoolExecutor(max_workers=2) as e:
     e.submit(print_hello)
     e.submit(print_goodbye)

哪个更好

import asyncio

async def print_hello():
    while True:
        print('sleep 1')
        await asyncio.sleep(1) 


async def print_goodbye():
    while True:
        print('sleep 2')
        await asyncio.sleep(2) 


co1 = print_hello()  
co2 = print_goodbye()  
asyncio.get_event_loop().run_until_complete(asyncio.gather(co1, co2))   

asyncio.run(main()) 比 asyncio.get_event_loop().run_until_complete 更简洁

蓝牙rfcomm

rfcomm的使用,类似tcp, 也有port的概念,在rfcomm中,称为channel, channel可以从1到30
————-
L2CAP, 默认最大包是 672, 但可以协商到65535
在L2CAP中, port 被称作psm(Protocol Service Multiplexers), 从1到32767的奇数。
——————
通过 Service Discovery Protocol 来寻找端口号

Linux下蓝牙工具

$   hciconfig 
hci0:	Type: Primary  Bus: USB
	BD Address: E1:E2:E3:E7:E8:91  ACL MTU: 1021:4  SCO MTU: 96:6
	UP RUNNING PSCAN ISCAN 
	RX bytes:16759 acl:20 sco:0 events:2538 errors:0
	TX bytes:605245 acl:20 sco:0 commands:2498 errors:0

关闭 
hciconfig hci0 down
开启
hciconfig hci0 up
查看设备名称
 hciconfig hci0 name
hci0:	Type: Primary  Bus: USB
	BD Address: E1:E2:E3:E7:E8:91  ACL MTU: 1021:4  SCO MTU: 96:6
	Name: 'radio'

这笔记本上确实是usb的蓝牙适配器

$  lsusb
Bus 001 Device 004: ID 8087:0a2b Intel Corp. Bluetooth wireless interface

hcitool用来搜索设备,或者显示底层的连接

hcitool scan
...
hcitool con

sdptool用来搜索并浏览 SDP服务, 或者配置本机的SDP

 $ sdptool browse  88:BB:99:11:22:33

Browsing 88:BB:99:11:22:33 ...

Service RecHandle: 0x10001
Service Class ID List:
  "PnP Information" (0x1200)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 1
  "SDP" (0x0001)
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "PnP Information" (0x1200)
    Version: 0x0100

Failed to connect to SDP server on 88:BB:99:11:22:33 Connection refused
Service Name: SerialPort
Service RecHandle: 0x10007
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 6
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "Serial Port" (0x1101)
    Version: 0x0100

而 hcidump 则类似于 tcpdump
l2ping类似于ping

l2ping  88:BB:99:11:22:33
Ping: 88:BB:99:11:22:33 from E1:E2:E3:E7:E8:91 (data size 44) ...
0 bytes from 88:1B:99:16:F6:AD id 0 time 7.11ms
0 bytes from 88:1B:99:16:F6:AD id 1 time 5.95ms
0 bytes from 88:1B:99:16:F6:AD id 2 time 4.80ms
0 bytes from 88:1B:99:16:F6:AD id 3 time 4.66ms

发送 echo 包到另一台设备
echo包是一个特殊类型的 LCAP包

rfcomm命令

rfcomm bind /dev/rfcomm0 88:BB:99:11:22:33 6

将channel 6 绑定到 /dev/rfcomm0 上,形成一个虚拟 serial port

rfcomm bind 6 98:D3:31:FC:73:2D

将channel 6 绑定到 /dev/rfcomm6上

或者不绑定,直接连接

rfcomm connect /dev/rfcomm0 88:BB:99:11:22:33 6

在 Python 3.3 之后 socket,直接支持蓝牙

import socket
import binascii

macAddr = '88:BB:99:11:22:33'
port = 6

s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((macAddr, port))

s.send(binascii.unhexlify('1200AAAAAA9669000B0600FFF20000000000')) 
data = s.recv(4)
print(data)

如果不支持AF_BLUETOOTH, 也可以用串口, 但是必须 先绑定正确的channel

import serial

DEVICE = '/dev/rfcomm6'
BAUD_RATE = 115200

s = serial.Serial(DEVICE, BAUD_RATE)
print('Connect to', DEVICE)

# Send data
s.write(b'hello\n')

# Receive data
data = s.read(3)
print(data)