深入理解7816(3)-----关于T=0 【转】
本文转载自:http://blog.sina.com.cn/s/blog_4df8400a0102vcyp.html
深入理解7816(3)-----关于T=0
卡片和终端之间的数据传输是通过命令响应的方式进行的,卡片只能被动地接收命令,并且给出响应。所有的命令都是以命令头开始,而该命令被完整地执行后(无论结果对错),必须以包含状态字(SW1 SW2)的响应结束。卡片和终端之间如何具体进行数据传输的,就依靠不同的通讯协议来实现,其中主要有T=0、T=1、T=CL。其中T=0和T=1适用于接触式卡片(7816),而T=CL适用于非接触式卡片(14443)。
T=0是按照单个字符的方式实现智能卡和终端数据传输的通讯协议,理解协议最好的方式就是尝试自己来设计协议,我们先看看该如何实现字符的传输。
假设让你来完成数据的正确接收,首先要解决的应该是从哪开始接收的问题。所以我们需要定义一个叫做“起始位”的标志,一旦发现“起始位”就可以确定一个全新的字符传过来了。然后就按照etu的约定逐位接收一个完整字节的数据。在接收的过程中万一有某一位识别错了,比如把“1”识别成了“0”怎么办?所以还要有一个校验位,看看之前接收的数据位是否有错误。如果有错误,则需要反馈一个出错信息,那么发送方会把刚才那个出错的字节再重新发送一次,如果接收再次出错,则要求发送方再重发,最多需要重发多少次由具体的应用规范来确定;如果没有错误,则反馈一个接收正确的状态,然后准备接收下一个字符。
因为7816的数据传输采用的是串行半双工的方式,而且数据的收发都通过一条I/O信号线来完成,所以在I/O线上传递的数据就有两个方向:可能是从终端到卡(“命令”),也可能是从卡到终端(“响应”)。如果两个连续字符的传输方向一致则称为“同向连续字符”,反之则称为“反向连续字符”。为了保证数据的正确接收,需要规定传递两个连续字符(无论是同向还是反向)的最小和最大时间间隔,最小间隔是为了让接收方有足够的时间准备接收下一个字符,而最大间隔是为了避免接收方无限制地等待,不然就算你已痴痴地等待了千年,那个“良人”还是不来。
那究竟需要接收多少个字符才算完成了一次完整的数据收发过程呢?怎么来判断发送方发送的动作已经“over”了呢?对于T=0协议而言,卡片和终端必须清楚彼此之间每次数据通讯需要收发多少字节的数据。可是有的时候终端在开始发送命令的那一刻是真不知道,比如在读一条记录时,终端事先可能并不知道这条记录的长度,那该怎么办?其实也没有别的好办法,只能靠试错的方式来投石问路。
首先,终端发给卡片的命令必须包含5个字节(分别是CLA + INS + P1 + P2 + P3,其中INS表示指令,说明该命令的作用)的命令头,也就是说卡片必须要收到来自终端的5个字节的命令头,才能决定下一步该干什么,然后根据做出的决定给终端一个反馈意见。同样终端在发送完5个字节的命令头后,也需要等待来自卡片的反馈后,再决定下一步该干什么。
这个反馈的信息一共有三类:1)空操作的过程字节NULL(0x60);2)响应字节ACK;3)状态字的第一个字节SW1。
NULL字节相当于网络通信中的“心跳包”,就是告诉终端不要进行超时处理,继续等待,而且NULL字节不见得是在卡片接收5个字节的命令头后反馈,通常在卡片需要进行大量数据更新或者复杂的加解密运算时,来不及返回状态字(SW)时先发一个0x60;
ACK是用来决定后续的数据传输的,不仅能决定传输的方向,还能决定后续传输字节的多少。如果ACK = INS,则传输剩余的全部字节,如果ACK = INS ^ FF,则传输后续的一个字节(早期版本的7816规范中还有ACK = INS ^ 01,以及ACK = INS ^ FE的定义,用来说明编程电压Vpp的,后来的版本中因为Vpp已经不再使用就把这两个值剔除了);
SW1只能是“0x9X”以及“0x6X”(不能是空操作字节“0x60”),如果卡片返回的是SW1,那么接下来必须紧跟着另一个状态字节SW2,标志着卡片对于该命令处理的结束,如果不断电,卡片在发送完SW2之后会等待下一个命令的到来。另外,因为SW1只能是“0x6X”和“0x9X”,所以INS一定不能取值0x6X和0x9X。至于ISO/IEC JTC1/SC17 的人为什么选择6 9作为SW1的高4位,是不是他们有什么特别的偏好就不得而知了。
ACK里面为什么会有仅传输后续一个字节的情形,而不是一次传完后续的所有字节?主要是因为如果智能卡芯片没有那么多可以用于临时缓存命令数据的RAM空间的话,就需要接收一个字节先处理一个字节,直到剩余的字节能被一次性地处理了,再反馈一个INS作为过程字节,把剩余的字节全部收进来。
除了这5个字节的命令头之外,可能还包含终端发送给卡片的命令数据(命令数据长度表示为Lc),也可能还需要卡片返回响应数据(响应数据长度表示为Le)。于是可以把终端和卡片之间的不同命令类型分为4类:1)只有命令头,没有命令数据也没有响应数据;2)有命令头没有命令数据,但是有响应数据;3)有命令头和命令数据,没有响应数据;4)有命令头、命令数据和响应数据。
对于情形1,P3=00;对于情形2,P3=Le,如果事先不知道Le的具体数值,可以填“00”,卡片会返回“0x6CXY”,其中的“XY”就是Le的正确值,终端用“XY”替换原来作为Le的“00”重新再发一遍命令就能正确获得响应数据了(就是前面提到的试错);对于情形3,P3=Lc;对于情形4,P3=Lc,卡片会返回“0x61XY”,其中的“XY”用来表示Le,然后终端再利用取响应的指令,获得卡片返回的响应数据。
上面说的这些命令和响应严格来说是属于TPDU(传输层协议数据单元)的概念,和通常智能卡规范里面说的APDU(应用层协议数据单元)概念是有差别的。因为应用层协议是不关心你的数据如何具体传输的,APDU只定义命令、命令数据、响应数据、返回状态,如何保证这些命令、数据的正确传输则是TPDU该做的事情。新的7816规范对于包含命令数据和响应数据的情形2、3、4还定义了扩展情形2E、3E、4E,用来处理数据长度Lc和Le大于256的场景,但是目前支持该扩展的卡片和终端还不多。