implement TPDU card reader
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 5 Oct 2016 11:00:22 +0000 (20:00 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 5 Oct 2016 11:00:22 +0000 (20:00 +0900)
tests/card_reader.py

index 70825ab..67f4276 100644 (file)
@@ -36,6 +36,49 @@ def ccid_compose(msg_type, seq, slot=0, rsv=0, param=0, data=b""):
 
 IFSC=254
 
+def compute_edc(pcb, info):
+    edc = pcb
+    edc ^= len(info)
+    for i in range(len(info)):
+        edc ^= info[i]
+    return edc
+
+def compose_i_block(ns, info, more):
+    pcb = 0x00
+    if ns:
+        pcb |= 0x40
+    if more:
+        pcb |= 0x20
+    edc = compute_edc(pcb, info)
+    return bytes([0, pcb, len(info)]) + info + bytes([edc])
+
+def compose_r_block(nr, edc_error=0):
+    pcb = 0x80
+    if nr:
+        pcb |= 0x10
+    if edc_error:
+        pcb |= 0x01
+    return bytes([0, pcb, 0, pcb])
+
+def is_r_block_no_error(blk):
+    return ((blk[1] & 0xC0) == 0x80 and (blk[1] & 0x2f) == 0x00)
+
+def is_s_block_time_ext(blk):
+    return (blk[1] == 0xC3)
+
+def is_i_block_last(blk):
+    return ((blk[1] & 0x80) == 0 and (blk[1] & 0x20) == 0)
+
+def is_i_block_more(blk):
+    return ((blk[1] & 0x80) == 0 and (blk[1] & 0x20) == 0x20)
+
+def is_edc_error(blk):
+    # to be implemented
+    return 0
+
+def i_block_content(blk)
+    return blk[3:-1]
+
 class CardReader(object):
     def __init__(self, dev):
         """
@@ -130,18 +173,20 @@ class CardReader(object):
         #
         if self.__use_APDU == False:
             # TPDU reader configuration
+            self.ns = 0
+            self.nr = 0
             pps = b"\xFF\x11\x18\xF6"
-            ret_pps = self.ccid_send_data_block(pps)
+            status, chain, ret_pps = self.ccid_send_data_block(pps)
             #
             param = b"\x18\x10\xFF\x75\x00\xFE\x00"
             # ^--- This shoud be adapted by ATR string, see update_param_by_atr
             msg = ccid_compose(0x6d, self.__seq, rsv=0x1, data=param)
             self.__dev.write(self.__bulkout, msg, self.__timeout)
             self.increment_seq()
-            ret_param = self.ccid_get_result()
-            # Send an S-block
+            status, chain, ret_param = self.ccid_get_result()
+            # Send an S-block of changing IFSD=254
             sblk = b"\x00\xC1\x01\xFE\x3E"
-            ret_sblk = self.ccid_send_data_block(sblk)
+            status, chain, ret_sblk = self.ccid_send_data_block(sblk)
         return self.atr
 
     def ccid_power_off(self):
@@ -183,11 +228,66 @@ class CardReader(object):
         else:
             raise ValueError("ccid_send_cmd")
 
+    def send_tpdu(self, info=None, more=0, response_time_ext=0,
+                  edc_error=0, no_error=0):
+        if info:
+            data = compose_i_block(self.ns, info, more)
+        elif response_time_ext:
+            # compose S-block
+            data = b"\x00\xE3\x00\xE3"
+        elif edc_error:
+            data = compose_r_block(self.nr, edc_error=1)
+        elif no_error:
+            data = compose_r_block(self.nr)
+        msg = ccid_compose(0x6f, self.__seq, data=data)
+        self.__dev.write(self.__bulkout, msg, self.__timeout)
+        self.increment_seq()
+
+    def recv_tpdu(self):
+        status, chain, data = self.ccid_get_result()
+        return data
+
     def send_cmd(self, cmd):
+        # Simple APDU case
         if self.__use_APDU:
             return self.ccid_send_cmd(cmd)
-        # TPDU
-        raise ValueError("TPDU not implemented yet")
+        # TPDU case
+        while len(cmd) > 254:
+            blk = cmd[0:253]
+            cmd = cmd[254:]
+            while True:
+                self.send_tpdu(info=blk,more=1)
+                rblk = self.recv_tpdu()
+                if is_r_block_no_error(rblk):
+                    break
+            self.ns = self.ns ^ 1
+        while True:
+            self.send_tpdu(info=cmd)
+            blk = self.recv_tpdu()
+            if is_r_block_no_error(blk):
+                break
+        self.ns = self.ns ^ 1
+        res = b""
+        while True:
+            if is_s_block_time_ext(blk):
+                self.send_tpdu(response_time_ext=1)
+            elif is_i_block_last(blk):
+                self.nr = self.nr ^ 1
+                if is_edc_error(blk):
+                    self.send_tpdu(edc_error=1)
+                else:
+                    res += i_block_content(blk)
+                    break
+            elif is_i_block_more(blk):
+                self.nr = self.nr ^ 1
+                if is_edc_error(blk):
+                    self.send_tpdu(edc_error=1)
+                else:
+                    res += i_block_content(blk)
+                    self.send_tpdu(no_error=1)
+            blk = self.recv_tpdu()
+        return res
+
 
 class find_class(object):
     def __init__(self, usb_class):