def _read_resp(self, cid, cmd): resp = b'.' header = cid + int2byte(TYPE_INIT | cmd) while resp and resp[:5] != header: resp_vals = _read_timeout(self.handle, HID_RPT_SIZE) resp = b''.join(int2byte(v) for v in resp_vals) if resp[:5] == cid + int2byte(STAT_ERR): raise U2FHIDError(byte2int(resp[7])) if not resp: raise exc.DeviceError("Invalid response from device!") data_len = (byte2int(resp[5]) << 8) + byte2int(resp[6]) data = resp[7:min(7 + data_len, HID_RPT_SIZE)] data_len -= len(data) seq = 0 while data_len > 0: resp_vals = _read_timeout(self.handle, HID_RPT_SIZE) resp = b''.join(int2byte(v) for v in resp_vals) if resp[:4] != cid: raise exc.DeviceError("Wrong CID from device!") if byte2int(resp[4]) != seq & 0x7f: raise exc.DeviceError("Wrong SEQ from device!") seq += 1 new_data = resp[5:min(5 + data_len, HID_RPT_SIZE)] data_len -= len(new_data) data += new_data return data
def _read_resp(self, cid, cmd): resp = '.' header = cid + chr(TYPE_INIT | cmd) while resp and resp[:5] != header: resp = ''.join(map(chr, _read_timeout(self.handle, HID_RPT_SIZE))) if resp[:5] == cid + chr(STAT_ERR): raise U2FHIDError(ord(resp[6])) if not resp: raise exc.DeviceError("Invalid response from device!") data_len = (ord(resp[5]) << 8) + ord(resp[6]) data = resp[7:min(7 + data_len, HID_RPT_SIZE)] data_len -= len(data) seq = 0 while data_len > 0: resp = ''.join(map(chr, _read_timeout(self.handle, HID_RPT_SIZE))) if resp[:4] != cid: raise exc.DeviceError("Wrong CID from device!") if ord(resp[4]) != seq & 0x7f: raise exc.DeviceError("Wrong SEQ from device!") seq += 1 new_data = resp[5:min(5 + data_len, HID_RPT_SIZE)] data_len -= len(new_data) data += new_data return data
def send_apdu(self, ins, p1=0, p2=0, data=b''): """ Sends an APDU to the device, and waits for a response. """ if data is None: data = b'' elif isinstance(data, int): data = int2byte(data) size = len(data) l0 = size >> 16 & 0xff l1 = size >> 8 & 0xff l2 = size & 0xff apdu_data = struct.pack('B B B B B B B %is B B' % size, 0, ins, p1, p2, l0, l1, l2, data, 0x04, 0x00) try: resp = self._do_send_apdu(apdu_data) except Exception as e: # TODO Use six.reraise if/when Six becomes an agreed dependency. raise exc.DeviceError(e) status = struct.unpack('>H', resp[-2:])[0] data = resp[:-2] if status != APDU_OK: raise exc.APDUError(status) return data
def send_apdu(self, ins, p1=0, p2=0, data=''): """ Sends an APDU to the device, and waits for a response. """ if data is None: data = '' elif isinstance(data, int): data = chr(data) size = len(data) l0 = size >> 16 & 0xff l1 = size >> 8 & 0xff l2 = size & 0xff apdu_data = "%c%c%c%c%c%c%c%s%c%c" % \ (0, ins, p1, p2, l0, l1, l2, data, 0x04, 0x00) try: resp = self._do_send_apdu(apdu_data) except Exception as e: # Wrap exception, keeping trace raise exc.DeviceError(e), None, sys.exc_info()[2] status = int(resp[-2:].encode('hex'), 16) data = resp[:-2] if status != APDU_OK: raise exc.APDUError(status) return data
def _send_req(self, cid, cmd, data): size = len(data) bc_l = int2byte(size & 0xff) bc_h = int2byte(size >> 8 & 0xff) payload = cid + int2byte(TYPE_INIT | cmd) + bc_h + bc_l + \ data[:HID_RPT_SIZE - 7] payload += b'\0' * (HID_RPT_SIZE - len(payload)) if self.handle.write([0] + [byte2int(c) for c in payload]) < 0: raise exc.DeviceError("Cannot write to device!") data = data[HID_RPT_SIZE - 7:] seq = 0 while len(data) > 0: payload = cid + int2byte(0x7f & seq) + data[:HID_RPT_SIZE - 5] payload += b'\0' * (HID_RPT_SIZE - len(payload)) if self.handle.write([0] + [byte2int(c) for c in payload]) < 0: raise exc.DeviceError("Cannot write to device!") data = data[HID_RPT_SIZE - 5:] seq += 1
def _write_to_device(self, to_send, timeout=2.0): expected = len(to_send) actual = 0 stop_at = time() + timeout while actual != expected: if (time() > stop_at): raise exc.DeviceError("Unable to send data to the device") actual = self.handle.write(to_send) sleep(0.025)
def init(self): nonce = os.urandom(8) resp = self.call(CMD_INIT, nonce) timeout = time() + 2.0 while (len(resp) != 17 or resp[:8] != nonce): if timeout < time(): raise exc.DeviceError('Wrong INIT response from device') sleep(0.1) resp = self._read_resp(self.cid, CMD_INIT) self.cid = resp[8:12] self.capabilities = byte2int(resp[16])
def ping(self, msg=b'Hello U2F'): resp = self.call(CMD_PING, msg) if resp != msg: raise exc.DeviceError("Incorrect PING readback") return resp