def der_read(der_data, expected_t=None): t = byte2int(der_data[0]) if expected_t is not None and expected_t != t: raise ValueError('Wrong tag. Expected: %x, got: %x' % (expected_t, t)) l = byte2int(der_data[1]) offs = 2 if l > 0x80: n_bytes = l - 0x80 l = b2len(der_data[offs:offs + n_bytes]) offs = offs + n_bytes v = der_data[offs:offs + l] rest = der_data[offs + l:] if expected_t is None: return t, v, rest return v, rest
def send_apdu(self, cl, ins, p1, p2, data): header = [cl, ins, p1, p2, len(data)] # from binascii import b2a_hex # print("SEND:", b2a_hex(''.join(map(int2byte, header)) + data)) resp, sw1, sw2 = self._conn.transmit(header + [byte2int(b) for b in data]) # print("RECV:", b2a_hex(b''.join(map(int2byte, resp + [sw1, sw2])))) return b''.join(int2byte(i) for i in resp), sw1 << 8 | sw2
def format_truncated(t_resp, scheme=SCHEME_STANDARD): digits, data = byte2int(t_resp[0]), t_resp[1:] int_data = parse_truncated(data) if scheme == SCHEME_STANDARD: return format_code(int_data, digits) elif scheme == SCHEME_STEAM: return format_code_steam(int_data, digits)
def list(self): ensure_unlocked(self) resp = self._send(INS_LIST) items = [] while resp: data, resp = der_read(resp, TAG_NAME_LIST) items.append(Credential(self, TYPE_MASK & byte2int(data[0]), data[1:], None)) return items
def calculate(self, name, oath_type, timestamp=None): challenge = time_challenge(timestamp) if oath_type == TYPE_TOTP else '' data = der_pack(TAG_NAME, name.encode('utf8'), TAG_CHALLENGE, challenge) resp = self._send(INS_CALCULATE, data) # Manual dynamic truncation required for Steam entries resp = der_read(resp, TAG_RESPONSE)[0] digits, resp = resp[0:1], resp[1:] offset = byte2int(resp[-1]) & 0xF resp = resp[offset:offset + 4] scheme = SCHEME_STEAM if name.startswith('Steam:') else SCHEME_STANDARD return format_truncated(digits + resp, scheme)
def parse_full(resp): offs = byte2int(resp[-1]) & 0xf return parse_truncated(resp[offs:offs+4])
def b2len(bs): l = 0 for b in bs: l *= 256 l += byte2int(b) return l
def version(self): return tuple(byte2int(d) for d in self._version)
def parse_full(resp): offs = byte2int(resp[-1]) & 0xf return parse_truncated(resp[offs:offs + 4])
def is_dark(color): # If any R, G, or B value is < 200 we consider it dark. return any(byte2int(c) < 200 for c in color)