Ejemplo n.º 1
0
    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 = 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[6]))

        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:5]) != 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
Ejemplo n.º 3
0
    def exchange(self, apdu, timeout=TIMEOUT):
        if self.debug:
            print("U2F => %s" % apdu.hex())

        if len(apdu) >= 256:
            raise CommException("Too long APDU to transport")

        # wrap apdu
        i = 0
        keyHandle = b''
        while i < len(apdu):
            val = apdu[i:i + 1]
            if len(self.scrambleKey) > 0:
                val = b'' + int2byte(
                    ord(val)
                    ^ ord(self.scrambleKey[i % len(self.scrambleKey)]))
            keyHandle += val
            i += 1

        client_param = sha256("u2f_tunnel".encode('utf8')).digest()
        app_param = sha256("u2f_tunnel".encode('utf8')).digest()

        request = client_param + app_param + int2byte(
            len(keyHandle)) + keyHandle

        start = time.time()
        while time.time() - start < timeout:

            #p1 = 0x07 if check_only else 0x03
            p1 = 0x03
            p2 = 0
            try:
                response = self.device.send_apdu(INS_SIGN, p1, p2, request)
            except exc.APDUError as e:
                if e.code == 0x6985:
                    time.sleep(0.25)
                    continue
                raise e

            if self.debug:
                print("U2F <= %s%.2x" % (response.hex(), 0x9000))

            # check replied status words of the command (within the APDU tunnel)
            if response[-2:] != b"\x90\x00":
                raise CommException("Invalid status words received: " +
                                    response[-2:].hex())
            else:
                break

        # api expect a byte array, remove the appended status words, remove the user presence and counter
        return bytearray(response[5:-2])
Ejemplo n.º 4
0
 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))
     self.handle.write([0] + [byte2int(c) for c in payload])
     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))
         self.handle.write([0] + [byte2int(c) for c in payload])
         data = data[HID_RPT_SIZE - 5:]
         seq += 1
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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, 0x00, 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_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))
     self.handle.write([0] + [byte2int(c) for c in payload])
     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))
         self.handle.write([0] + [byte2int(c) for c in payload])
         data = data[HID_RPT_SIZE - 5:]
         seq += 1
Ejemplo n.º 8
0
def authenticate(device, data, facet, check_only=False):
    """
    Signs an authentication challenge

    data = {
        'version': "U2F_V2",
        'challenge': websafe_encode(self.challenge),
        'appId': self.binding.app_id,
        'keyHandle': websafe_encode(self.binding.key_handle)
    }

    """

    if isinstance(data, string_types):
        data = json.loads(data)

    if data['version'] != VERSION:
        raise ValueError('Unsupported U2F version: %s' % data['version'])

    app_id = data.get('appId', facet)
    verify_facet(app_id, facet)
    app_param = sha256(app_id.encode('utf8')).digest()

    key_handle = websafe_decode(data['keyHandle'])

    # Client data
    client_data = {
        'typ': 'navigator.id.getAssertion',
        'challenge': data['challenge'],
        'origin': facet
    }
    client_data = json.dumps(client_data)
    client_param = sha256(client_data.encode('utf8')).digest()

    request = client_param + app_param + int2byte(
        len(key_handle)) + key_handle

    p1 = 0x07 if check_only else 0x03
    p2 = 0
    response = device.send_apdu(INS_SIGN, p1, p2, request)

    return {
        'clientData': websafe_encode(client_data),
        'signatureData': websafe_encode(response),
        'keyHandle': data['keyHandle']
    }
Ejemplo n.º 9
0
def authenticate(device, data, facet, check_only=False):
    """
    Signs an authentication challenge

    data = {
        'version': "U2F_V2",
        'challenge': websafe_encode(self.challenge),
        'appId': self.binding.app_id,
        'keyHandle': websafe_encode(self.binding.key_handle)
    }

    """

    if isinstance(data, string_types):
        data = json.loads(data)

    if data['version'] != VERSION:
        raise ValueError('Unsupported U2F version: %s' % data['version'])

    app_id = data.get('appId', facet)
    verify_facet(app_id, facet)
    app_param = sha256(app_id.encode('utf8')).digest()

    key_handle = websafe_decode(data['keyHandle'])

    # Client data
    client_data = {
        'typ': 'navigator.id.getAssertion',
        'challenge': data['challenge'],
        'origin': facet
    }
    client_data = json.dumps(client_data)
    client_param = sha256(client_data.encode('utf8')).digest()

    request = client_param + app_param + int2byte(len(key_handle)) + key_handle

    p1 = 0x07 if check_only else 0x03
    p2 = 0
    response = device.send_apdu(INS_SIGN, p1, p2, request)

    return {
        'clientData': websafe_encode(client_data),
        'signatureData': websafe_encode(response),
        'keyHandle': data['keyHandle']
    }
Ejemplo n.º 10
0
    def _register(self, data):
        client_param = data[:32]
        app_param = data[32:]

        # ECC key generation
        privu = ec.generate_private_key(CURVE(), default_backend())
        pubu = privu.public_key()
        pub_key_der = pubu.public_bytes(
            serialization.Encoding.DER,
            serialization.PublicFormat.SubjectPublicKeyInfo,
        )
        pub_key = pub_key_der[-65:]

        # Store
        key_handle = os.urandom(64)
        priv_key_pem = privu.private_bytes(
            serialization.Encoding.PEM,
            serialization.PrivateFormat.PKCS8,
            serialization.NoEncryption(),
        )
        self.data['keys'][_b16text(key_handle)] = {
            'priv_key': priv_key_pem.decode('ascii'),
            'app_param': _b16text(app_param),
        }
        self._persist()

        # Attestation signature
        cert = CERT
        cert_priv = serialization.load_pem_private_key(
            CERT_PRIV,
            password=None,
            backend=default_backend(),
        )
        signer = cert_priv.signer(ec.ECDSA(hashes.SHA256()))
        signer.update(b'\x00' + app_param + client_param + key_handle +
                      pub_key)
        signature = signer.finalize()

        raw_response = b'\x05' + pub_key + int2byte(len(key_handle)) + \
            key_handle + cert + signature

        return raw_response
Ejemplo n.º 11
0
    def _register(self, data):
        client_param = data[:32]
        app_param = data[32:]

        # ECC key generation
        privu = ec.generate_private_key(CURVE(), default_backend())
        pubu = privu.public_key()
        pub_key_der = pubu.public_bytes(
            serialization.Encoding.DER,
            serialization.PublicFormat.SubjectPublicKeyInfo,
        )
        pub_key = pub_key_der[-65:]

        # Store
        key_handle = os.urandom(64)
        priv_key_pem = privu.private_bytes(
            serialization.Encoding.PEM,
            serialization.PrivateFormat.PKCS8,
            serialization.NoEncryption(),
        )
        self.data['keys'][_b16text(key_handle)] = {
            'priv_key': priv_key_pem.decode('ascii'),
            'app_param': _b16text(app_param),
        }
        self._persist()

        # Attestation signature
        cert = CERT
        cert_priv = serialization.load_pem_private_key(
            CERT_PRIV, password=None, backend=default_backend(),
        )
        signer = cert_priv.signer(ec.ECDSA(hashes.SHA256()))
        signer.update(
            b'\x00' + app_param + client_param + key_handle + pub_key
        )
        signature = signer.finalize()

        raw_response = b'\x05' + pub_key + int2byte(len(key_handle)) + \
            key_handle + cert + signature

        return raw_response
Ejemplo n.º 12
0
    def exchange(self, apdu, timeout=TIMEOUT):
        if self.debug:
            print("U2F => %s" % hexstr(apdu))

        if (len(apdu) >= 256):
            raise CommException("Too long APDU to transport")

        # wrap apdu
        i = 0
        keyHandle = ""
        while i < len(apdu):
            val = apdu[i:i + 1]
            if len(self.scrambleKey) > 0:
                val = chr(
                    ord(val)
                    ^ ord(self.scrambleKey[i % len(self.scrambleKey)]))
            keyHandle += val
            i += 1

        client_param = sha256("u2f_tunnel".encode('utf8')).digest()
        app_param = sha256("u2f_tunnel".encode('utf8')).digest()

        request = client_param + app_param + int2byte(
            len(keyHandle)) + keyHandle

        #p1 = 0x07 if check_only else 0x03
        p1 = 0x03
        p2 = 0
        response = self.device.send_apdu(INS_SIGN, p1, p2, request)

        if self.debug:
            print("U2F <= %s%.2x" % (hexstr(response), 0x9000))

        # check replied status words of the command (within the APDU tunnel)
        if hexstr(response[-2:]) != "9000":
            raise CommException("Invalid status words received: " +
                                hexstr(response[-2:]))

        # api expect a byte array, remove the appended status words
        return bytearray(response[:-2])
Ejemplo n.º 13
0
  def exchange(self, apdu, timeout=TIMEOUT):
    if self.debug:
      print("U2F => %s" % hexstr(apdu))

    if (len(apdu)>=256):
      raise CommException("Too long APDU to transport")  
    
    # wrap apdu
    i=0
    keyHandle = ""
    while i < len(apdu):
      val = apdu[i:i+1]
      if len(self.scrambleKey) > 0:
        val = chr(ord(val) ^ ord(self.scrambleKey[i % len(self.scrambleKey)]))
      keyHandle += val
      i+=1
    
    client_param = sha256("u2f_tunnel".encode('utf8')).digest()
    app_param = sha256("u2f_tunnel".encode('utf8')).digest()

    request = client_param + app_param + int2byte(len(keyHandle)) + keyHandle

    #p1 = 0x07 if check_only else 0x03
    p1 = 0x03
    p2 = 0
    response = self.device.send_apdu(INS_SIGN, p1, p2, request)

    if self.debug:
      print("U2F <= %s%.2x" % (hexstr(response), 0x9000))

    # check replied status words of the command (within the APDU tunnel)
    if hexstr(response[-2:]) != "9000":
      raise CommException("Invalid status words received: " + hexstr(response[-2:]));

    # api expect a byte array, remove the appended status words
    return bytearray(response[:-2])
Ejemplo n.º 14
0
    def call(self, cmd, data=b''):
        if isinstance(data, int):
            data = int2byte(data)

        self._send_req(self.cid, cmd, data)
        return self._read_resp(self.cid, cmd)
    def call(self, cmd, data=b''):
        if isinstance(data, int):
            data = int2byte(data)

        self._send_req(self.cid, cmd, data)
        return self._read_resp(self.cid, cmd)