async Task TryUnlockWorkstation(IDevice device, string flowId, Action <WorkstationUnlockResult> onUnlockAttempt) { var result = new WorkstationUnlockResult(); await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Unlock.ReadingCredentials"], device.Mac); var credentials = await GetCredentials(device); // send credentials to the Credential Provider to unlock the PC await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Unlock.Unlocking"], device.Mac); result.IsSuccessful = await _workstationUnlocker .SendLogonRequest(credentials.Login, credentials.Password, credentials.PreviousPassword); result.AccountName = credentials.Name; result.AccountLogin = credentials.Login; result.DeviceMac = device.Mac; result.FlowId = flowId; onUnlockAttempt?.Invoke(result); if (!result.IsSuccessful) { throw new WorkstationUnlockFailedException(); // Abort connection flow } }
async Task UnlockByRfid(string rfid) { if (!isRunning) { return; } if (!_rfidSettingsManager.Settings.IsRfidEnabled) { return; } if (Interlocked.CompareExchange(ref _isConnecting, 1, 1) == 1) { return; } HwVaultShortInfoFromHesDto info = null; try { _screenActivator?.ActivateScreen(); if (_hesConnection == null) { throw new Exception(TranslationSource.Instance["ConnectionFlow.RfidConnection.Error.NotConnectedToHes"]); } // get MAC address from the HES info = await _hesConnection.GetHwVaultInfoByRfid(rfid); await _clientUiManager.SendNotification(TranslationSource.Instance["ConnectionFlow.RfidConnection.ContactingHesMessage"], info.VaultMac); if (Interlocked.CompareExchange(ref _isConnecting, 1, 0) == 0) { try { await _connectionFlowProcessor.ConnectAndUnlock(info.VaultMac, OnUnlockAttempt); } catch (Exception) { // Silent handling. Log is already printed inside of _connectionFlowProcessor.ConnectAndUnlock() } finally { // this delay allows a user to move away the device from the rfid // and prevents the repeated call of this method await Task.Delay(SdkConfig.DelayAfterMainWorkflow); Interlocked.Exchange(ref _isConnecting, 0); } } } catch (Exception ex) { WriteLine(ex); await _clientUiManager.SendError(HideezExceptionLocalization.GetErrorAsString(ex), info?.VaultMac); } }
public async Task AuthVault(IDevice device, CancellationToken ct) { ct.ThrowIfCancellationRequested(); if (device.AccessLevel.IsMasterKeyRequired && _hesConnection.State == HesConnectionState.Connected) { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.MasterKey.AwaitingHESAuth"], device.Mac); await _hesConnection.AuthHwVault(device.SerialNo); await device.RefreshDeviceInfo(); } if (device.AccessLevel.IsMasterKeyRequired) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.MasterKey.Error.AuthFailed"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.MasterKey.Error.AuthFailedNoNetwork"]); } } }
async Task <bool> ButtonWorkflow(IDevice device, int timeout, CancellationToken ct) { if (!device.AccessLevel.IsButtonRequired) { return(true); } await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Button.PressButtonMessage"], device.Mac); await _ui.ShowButtonConfirmUi(device.Id); var res = await device.WaitButtonConfirmation(timeout, ct); ct.ThrowIfCancellationRequested(); return(res); }
async Task MainWorkflow(string mac, bool rebondOnConnectionFail, bool tryUnlock, Action <WorkstationUnlockResult> onUnlockAttempt, CancellationToken ct) { // Ignore MainFlow requests for devices that are already connected // IsConnected-true indicates that device already finished main flow or is in progress var existingDevice = _deviceManager.Find(mac, (int)DefaultDeviceChannel.Main); if (existingDevice != null && existingDevice.IsConnected && !WorkstationHelper.IsActiveSessionLocked()) { return; } WriteLine($"Started main workflow ({mac})"); var flowId = Guid.NewGuid().ToString(); Started?.Invoke(this, flowId); bool workflowFinishedSuccessfully = false; bool deleteVaultBond = false; string errorMessage = null; IDevice device = null; try { await _ui.SendNotification(string.Empty, mac); _subp.PermissionsCheckProcessor.CheckPermissions(); // Start periodic screen activator to raise the "curtain" if (WorkstationHelper.IsActiveSessionLocked()) { _screenActivator?.ActivateScreen(); _screenActivator?.StartPeriodicScreenActivation(0); await new WaitWorkstationUnlockerConnectProc(_workstationUnlocker) .Run(SdkConfig.WorkstationUnlockerConnectTimeout, ct); } device = await _subp.VaultConnectionProcessor.ConnectVault(mac, rebondOnConnectionFail, ct); device.Disconnected += OnVaultDisconnectedDuringFlow; device.OperationCancelled += OnCancelledByVaultButton; await _subp.VaultConnectionProcessor.WaitVaultInitialization(mac, device, ct); if (device.IsBoot) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.Error.VaultInBootloaderMode"]); } device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Initializing); HwVaultInfoFromHesDto vaultInfo = new HwVaultInfoFromHesDto(); // Initializes with default values for when HES is not connected if (_hesConnection.State == HesConnectionState.Connected) { vaultInfo = await _hesConnection.UpdateHwVaultProperties(new HwVaultInfoFromClientDto(device), true); } _subp.CacheVaultInfoProcessor.CacheAndUpdateVaultOwner(ref device, vaultInfo, ct); await _subp.LicensingProcessor.CheckLicense(device, vaultInfo, ct); vaultInfo = await _subp.StateUpdateProcessor.UpdateVaultStatus(device, vaultInfo, ct); vaultInfo = await _subp.ActivationProcessor.ActivateVault(device, vaultInfo, ct); await _subp.MasterkeyProcessor.AuthVault(device, ct); var osAccUpdateTask = _subp.AccountsUpdateProcessor.UpdateAccounts(device, vaultInfo, true); if (_workstationUnlocker.IsConnected && WorkstationHelper.IsActiveSessionLocked() && tryUnlock) { await Task.WhenAll(_subp.UserAuthorizationProcessor.AuthorizeUser(device, ct), osAccUpdateTask); _screenActivator?.StopPeriodicScreenActivation(); await _subp.UnlockProcessor.UnlockWorkstation(device, flowId, onUnlockAttempt, ct); } else { await osAccUpdateTask; } device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Finalizing); WriteLine($"Finalizing main workflow: ({device.Id})"); DeviceFinilizingMainFlow?.Invoke(this, device); await _subp.AccountsUpdateProcessor.UpdateAccounts(device, vaultInfo, false); device.SetUserProperty(CustomProperties.HW_CONNECTION_STATE_PROP, HwVaultConnectionState.Online); if (_hesConnection.State == HesConnectionState.Connected) { await _hesConnection.UpdateHwVaultProperties(new HwVaultInfoFromClientDto(device), false); } workflowFinishedSuccessfully = true; } catch (HideezException ex) { switch (ex.ErrorCode) { case HideezErrorCode.DeviceIsLocked: case HideezErrorCode.DeviceNotAssignedToUser: case HideezErrorCode.HesDeviceNotFound: case HideezErrorCode.HesDeviceCompromised: case HideezErrorCode.DeviceHasBeenWiped: // There errors require bond removal deleteVaultBond = true; errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); break; case HideezErrorCode.ButtonConfirmationTimeout: case HideezErrorCode.GetPinTimeout: case HideezErrorCode.GetActivationCodeTimeout: // Silent handling WriteLine(ex); break; case HideezErrorCode.HesNotConnected: // We need to display an error message which is different from one that is usually shown for that error code. errorMessage = TranslationSource.Instance["ConnectionFlow.Error.UnexpectedlyLostNetworkConnection"]; break; default: errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); break; } } catch (VaultFailedToAuthorizeException ex) { // User should never receive this error unless there is a bug in algorithm errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); } catch (WorkstationUnlockFailedException ex) { // Silent handling of failed workstation unlock // The actual message will be displayed by credential provider WriteLine(ex); } catch (OperationCanceledException ex) { // Silent cancelation handling WriteLine(ex); } catch (TimeoutException ex) { // Silent timeout handling WriteLine(ex); } catch (WebSocketException ex) { // Retrieve the most inner WebSocketException to retrieve the error code if (ex.WebSocketErrorCode != 0) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], ex.WebSocketErrorCode); } else if (ex.ErrorCode != 0) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], ex.ErrorCode); } else if (ex.InnerException is Win32Exception) { errorMessage = string.Format(TranslationSource.Instance["ConnectionFlow.Error.UnexpectedNetworkError"], "native " + (ex.InnerException as Win32Exception).NativeErrorCode); } } catch (Exception ex) { errorMessage = HideezExceptionLocalization.GetErrorAsString(ex); } finally { if (device != null) { device.Disconnected -= OnVaultDisconnectedDuringFlow; device.OperationCancelled -= OnCancelledByVaultButton; } _screenActivator?.StopPeriodicScreenActivation(); } await WorkflowCleanup(errorMessage, mac, device, workflowFinishedSuccessfully, deleteVaultBond); Finished?.Invoke(this, flowId); WriteLine($"Main workflow end {mac}"); }
public async Task <HwVaultInfoFromHesDto> ActivateVault(IDevice device, HwVaultInfoFromHesDto vaultInfo, CancellationToken ct) { HwVaultInfoFromHesDto newVaultInfo = null; if (device.IsLocked && device.IsCanUnlock) { try { do { ct.ThrowIfCancellationRequested(); var code = await _ui.GetActivationCode(device.Id, 30_000, ct); // Todo: activation timeout should not be a magic number ct.ThrowIfCancellationRequested(); if (code.Length < 6) { await _ui.SendError(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.CodeToShort"], device.Mac); continue; } if (code.Length > 8) { await _ui.SendError(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.CodeToLong"], device.Mac); continue; } try { await device.UnlockDeviceCode(code); } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_PIN_WRONG) // Entered invalid activation code { } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_CODE) // Unlock attempts == 0 { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.LockedByInvalidAttempts"]); } ct.ThrowIfCancellationRequested(); await device.RefreshDeviceInfo(); ct.ThrowIfCancellationRequested(); if (!device.IsLocked) { WriteLine($"({device.SerialNo}) unlocked with activation code"); } else if (device.UnlockAttemptsRemain > 0) { await _ui.SendNotification(TranslationSource.Instance.Format("ConnectionFlow.ActivationCode.Error.InvalidCode", device.UnlockAttemptsRemain), device.Mac); } else { // We won't reach this line, but will leave it just in case throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.LockedByInvalidAttempts"]); } }while (device.IsLocked); } catch { if (_hesConnection.State == HesConnectionState.Connected) { await _hesConnection.UpdateHwVaultStatus(new HwVaultInfoFromClientDto(device), ct); } throw; } finally { await _ui.HideActivationCodeUi(); } if (_hesConnection.State == HesConnectionState.Connected) { newVaultInfo = await _hesConnection.UpdateHwVaultStatus(new HwVaultInfoFromClientDto(device), ct); } } if (device.IsLocked) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.VaultIsLocked"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.ActivationCode.Error.VaultIsLockedNoNetwork"]); } } return(newVaultInfo ?? vaultInfo); }
public async Task <IDevice> ConnectVault(string mac, bool rebondOnFail, CancellationToken ct) { ct.ThrowIfCancellationRequested(); if (_bondManager.Exists(mac)) { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Connection.Stage1"], mac); } else { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Connection.Stage1.PressButton"], mac); } bool ltkErrorOccured = false; IDevice device = null; try { device = await _deviceManager.ConnectDevice(mac, SdkConfig.ConnectDeviceTimeout); } catch (Exception ex) // Thrown when LTK error occurs in csr { WriteLine(ex); ltkErrorOccured = true; } if (device == null) { ct.ThrowIfCancellationRequested(); string ltk = ""; if (ltkErrorOccured) { ltk = "LTK error."; ltkErrorOccured = false; } if (_bondManager.Exists(mac)) { await _ui.SendNotification(ltk + TranslationSource.Instance["ConnectionFlow.Connection.Stage2"], mac); } else { await _ui.SendNotification(ltk + TranslationSource.Instance["ConnectionFlow.Connection.Stage2.PressButton"], mac); } try { device = await _deviceManager.ConnectDevice(mac, SdkConfig.ConnectDeviceTimeout / 2); } catch (Exception ex) // Thrown when LTK error occurs in csr { WriteLine(ex); ltkErrorOccured = true; } if (device == null && rebondOnFail) { ct.ThrowIfCancellationRequested(); // remove the bond and try one more time await _deviceManager.RemoveByMac(mac); if (ltkErrorOccured) { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Connection.Stage3.LtkError.PressButton"], mac); // TODO: Fix LTK error in CSR } else { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.Connection.Stage3.PressButton"], mac); } device = await _deviceManager.ConnectDevice(mac, SdkConfig.ConnectDeviceTimeout); } } if (device == null) { throw new WorkflowException(TranslationSource.Instance.Format("ConnectionFlow.Connection.ConnectionFailed", mac)); } return(device); }
public async Task CheckLicense(IDevice device, HwVaultInfoFromHesDto vaultInfo, CancellationToken ct) { if ((device.AccessLevel.IsLinkRequired || vaultInfo.NeedUpdateLicense) && _hesConnection.State == HesConnectionState.Connected) { await _ui.SendNotification(TranslationSource.Instance["ConnectionFlow.License.UpdatingLicenseMessage"], device.Mac); IList <HwVaultLicenseDto> licenses; if (device.AccessLevel.IsLinkRequired) { licenses = await _hesConnection.GetHwVaultLicenses(device.SerialNo, ct); WriteLine($"Received {licenses.Count} TOTAL licenses from HES"); } else { licenses = await _hesConnection.GetNewHwVaultLicenses(device.SerialNo, ct); WriteLine($"Received {licenses.Count} NEW licenses from HES"); } ct.ThrowIfCancellationRequested(); if (licenses.Count > 0) { for (int i = 0; i < licenses.Count; i++) { var license = licenses[i]; ct.ThrowIfCancellationRequested(); if (license.Data == null) { throw new WorkflowException(TranslationSource.Instance.Format("ConnectionFlow.License.Error.EmptyLicenseData", device.SerialNo)); } if (license.Id == null) { throw new WorkflowException(TranslationSource.Instance.Format("ConnectionFlow.License.Error.EmptyLicenseId", device.SerialNo)); } try { await device.LoadLicense(license.Data, SdkConfig.DefaultCommandTimeout); WriteLine($"Loaded license ({license.Id}) into vault ({device.SerialNo}) in available slot"); } catch (HideezException ex) when(ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_CODE || ex.ErrorCode == HideezErrorCode.ERR_DEVICE_LOCKED_BY_PIN) { await device.LoadLicense(i, license.Data, SdkConfig.DefaultCommandTimeout); WriteLine($"Loaded license ({license.Id}) into vault ({device.SerialNo}) into slot {i}"); } await _hesConnection.OnHwVaultLicenseApplied(device.SerialNo, license.Id); } } await device.RefreshDeviceInfo(); ct.ThrowIfCancellationRequested(); } if (device.LicenseInfo == 0) { if (_hesConnection.State == HesConnectionState.Connected) { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.License.Error.NoLicensesAvailable"]); } else { throw new WorkflowException(TranslationSource.Instance["ConnectionFlow.License.Error.CannotDownloadLicense"]); } } }