def __init__(self, hid_device, read_timeout_secs=3.0): self.hid_device = hid_device in_size = hid_device.GetInReportDataLength() out_size = hid_device.GetOutReportDataLength() if in_size != out_size: raise errors.HardwareError( 'unsupported device with different in/out packet sizes.') if in_size == 0: raise errors.HardwareError('unable to determine packet size') self.packet_size = in_size self.read_timeout_secs = read_timeout_secs self.logger = logging.getLogger('pyu2f.hidtransport') self.InternalInit()
def InternalRecv(self): """Receives a message from the device, including defragmenting it.""" first_read = self.InternalReadFrame() first_packet = UsbHidTransport.InitPacket.FromWireFormat( self.packet_size, first_read) data = first_packet.payload to_read = first_packet.size - len(first_packet.payload) seq = 0 while to_read > 0: next_read = self.InternalReadFrame() next_packet = UsbHidTransport.ContPacket.FromWireFormat( self.packet_size, next_read) if self.cid != next_packet.cid: # Skip over packets that are for communication with other clients. # HID is broadcast, so we see potentially all communication from the # device. For well-behaved devices, these should be BUSY messages # sent to other clients of the device because at this point we're # in mid-message transit. continue if seq != next_packet.seq: raise errors.HardwareError('Packets received out of order') # This packet for us at this point, so debit it against our # balance of bytes to read. to_read -= len(next_packet.payload) data.extend(next_packet.payload) seq += 1 # truncate incomplete frames data = data[0:first_packet.size] return (first_packet.cmd, data)
def Register(self, app_id, challenge, registered_keys): """Registers app_id with the security key. Executes the U2F registration flow with the security key. Args: app_id: The app_id to register the security key against. challenge: Server challenge passed to the security key. registered_keys: List of keys already registered for this app_id+user. Returns: RegisterResponse with key_handle and attestation information in it ( encoded in FIDO U2F binary format within registration_data field). Raises: U2FError: There was some kind of problem with registration (e.g. the device was already registered or there was a timeout waiting for the test of user presence). """ client_data = model.ClientData(model.ClientData.TYP_REGISTRATION, challenge, self.origin) challenge_param = self.InternalSHA256(client_data.GetJson()) app_param = self.InternalSHA256(app_id) for key in registered_keys: try: # skip non U2F_V2 keys if key.version != u'U2F_V2': continue resp = self.security_key.CmdAuthenticate( challenge_param, app_param, key.key_handle, True) # check_only mode CmdAuthenticate should always raise some # exception raise errors.HardwareError('Should Never Happen') except errors.TUPRequiredError: # This indicates key was valid. Thus, no need to register raise errors.U2FError(errors.U2FError.DEVICE_INELIGIBLE) except errors.InvalidKeyHandleError as e: # This is the case of a key for a different token, so we just ignore it. pass except errors.HardwareError as e: raise errors.U2FError(errors.U2FError.BAD_REQUEST, e) # Now register the new key for _ in range(30): try: resp = self.security_key.CmdRegister(challenge_param, app_param) return model.RegisterResponse(resp, client_data) except errors.TUPRequiredError as e: self.security_key.CmdWink() time.sleep(0.5) except errors.HardwareError as e: raise errors.U2FError(errors.U2FError.BAD_REQUEST, e) raise errors.U2FError(errors.U2FError.TIMEOUT)