diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index 9bf27e20..6b7c9a24 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -21,6 +21,7 @@ using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; +using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; @@ -55,6 +56,7 @@ namespace SafeExamBrowser.Client.UnitTests private Mock explorerShell; private Mock fileSystemDialog; private Mock hashAlgorithm; + private Mock integrityModule; private Mock logger; private Mock messageBox; private Mock operationSequence; @@ -84,6 +86,7 @@ namespace SafeExamBrowser.Client.UnitTests explorerShell = new Mock(); fileSystemDialog = new Mock(); hashAlgorithm = new Mock(); + integrityModule = new Mock(); logger = new Mock(); messageBox = new Mock(); operationSequence = new Mock(); @@ -110,6 +113,7 @@ namespace SafeExamBrowser.Client.UnitTests explorerShell.Object, fileSystemDialog.Object, hashAlgorithm.Object, + integrityModule.Object, logger.Object, messageBox.Object, operationSequence.Object, @@ -371,7 +375,7 @@ namespace SafeExamBrowser.Client.UnitTests }; messageBox.Setup(m => m.Show( - It.Is(s => s == args.Message), + It.Is(s => s == args.Message), It.Is(s => s == args.Title), It.Is(a => a == (MessageBoxAction) args.Action), It.Is(i => i == (MessageBoxIcon) args.Icon), diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index d5573a5d..0a614582 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -21,6 +21,7 @@ using SafeExamBrowser.Communication.Contracts.Events; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Configuration.Contracts.Cryptography; +using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; @@ -43,30 +44,32 @@ namespace SafeExamBrowser.Client { internal class ClientController { - private IActionCenter actionCenter; - private IApplicationMonitor applicationMonitor; - private ClientContext context; - private IDisplayMonitor displayMonitor; - private IExplorerShell explorerShell; - private IFileSystemDialog fileSystemDialog; - private IHashAlgorithm hashAlgorithm; - private ILogger logger; - private IMessageBox messageBox; - private IOperationSequence operations; - private IRuntimeProxy runtime; - private bool sessionLocked; - private Action shutdown; - private ISplashScreen splashScreen; - private ISystemMonitor systemMonitor; - private ITaskbar taskbar; - private IText text; - private IUserInterfaceFactory uiFactory; + private readonly IActionCenter actionCenter; + private readonly IApplicationMonitor applicationMonitor; + private readonly ClientContext context; + private readonly IDisplayMonitor displayMonitor; + private readonly IExplorerShell explorerShell; + private readonly IFileSystemDialog fileSystemDialog; + private readonly IHashAlgorithm hashAlgorithm; + private readonly IIntegrityModule integrityModule; + private readonly ILogger logger; + private readonly IMessageBox messageBox; + private readonly IOperationSequence operations; + private readonly IRuntimeProxy runtime; + private readonly Action shutdown; + private readonly ISplashScreen splashScreen; + private readonly ISystemMonitor systemMonitor; + private readonly ITaskbar taskbar; + private readonly IText text; + private readonly IUserInterfaceFactory uiFactory; private IBrowserApplication Browser => context.Browser; private IClientHost ClientHost => context.ClientHost; private IServerProxy Server => context.Server; private AppSettings Settings => context.Settings; + private bool sessionLocked; + internal ClientController( IActionCenter actionCenter, IApplicationMonitor applicationMonitor, @@ -75,6 +78,7 @@ namespace SafeExamBrowser.Client IExplorerShell explorerShell, IFileSystemDialog fileSystemDialog, IHashAlgorithm hashAlgorithm, + IIntegrityModule integrityModule, ILogger logger, IMessageBox messageBox, IOperationSequence operations, @@ -93,6 +97,7 @@ namespace SafeExamBrowser.Client this.explorerShell = explorerShell; this.fileSystemDialog = fileSystemDialog; this.hashAlgorithm = hashAlgorithm; + this.integrityModule = integrityModule; this.logger = logger; this.messageBox = messageBox; this.operations = operations; @@ -123,6 +128,7 @@ namespace SafeExamBrowser.Client RegisterEvents(); ShowShell(); AutoStartApplications(); + ScheduleIntegrityVerification(); var communication = runtime.InformClientReady(); @@ -215,12 +221,12 @@ namespace SafeExamBrowser.Client private void Taskbar_LoseFocusRequested(bool forward) { - this.Browser.Focus(forward); + Browser.Focus(forward); } private void Browser_LoseFocusRequested(bool forward) { - this.taskbar.Focus(forward); + taskbar.Focus(forward); } private void DeregisterEvents() @@ -307,6 +313,39 @@ namespace SafeExamBrowser.Client } } + private void ScheduleIntegrityVerification() + { + const int FIVE_MINUTES = 300000; + const int TEN_MINUTES = 600000; + + var timer = new System.Timers.Timer(); + + timer.AutoReset = false; + timer.Elapsed += (o, args) => + { + logger.Info($"Attempting to verify application integrity..."); + + if (integrityModule.TryVerifyCodeSignature(out var isValid)) + { + if (isValid) + { + logger.Info("Application integrity successfully verified."); + } + else + { + logger.Warn("Application integrity is compromised!"); + ShowLockScreen(text.Get(TextKey.LockScreen_IntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty()); + } + } + else + { + logger.Warn("Failed to verify application integrity!"); + } + }; + timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES); + timer.Start(); + } + private void ApplicationMonitor_ExplorerStarted() { logger.Info("Trying to terminate Windows explorer..."); @@ -798,7 +837,7 @@ namespace SafeExamBrowser.Client { var result = messageBox.Show(TextKey.MessageBox_Quit, TextKey.MessageBox_QuitTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question); var quit = result == MessageBoxResult.Yes; - + if (quit) { logger.Info("The user chose to terminate the application."); diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 3b6d191a..e5c63dda 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -18,7 +18,9 @@ using SafeExamBrowser.Communication.Contracts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Communication.Hosts; using SafeExamBrowser.Communication.Proxies; +using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Configuration.Cryptography; +using SafeExamBrowser.Configuration.Integrity; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.OperationModel; using SafeExamBrowser.Core.Operations; @@ -68,6 +70,7 @@ namespace SafeExamBrowser.Client private UserInterfaceMode uiMode; private IActionCenter actionCenter; + private IIntegrityModule integrityModule; private ILogger logger; private IMessageBox messageBox; private INativeMethods nativeMethods; @@ -94,6 +97,7 @@ namespace SafeExamBrowser.Client actionCenter = uiFactory.CreateActionCenter(); context = new ClientContext(); + integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule))); messageBox = BuildMessageBox(); nativeMethods = new NativeMethods(); networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods); @@ -143,6 +147,7 @@ namespace SafeExamBrowser.Client explorerShell, fileSystemDialog, hashAlgorithm, + integrityModule, logger, messageBox, sequence, @@ -212,7 +217,7 @@ namespace SafeExamBrowser.Client private IOperation BuildBrowserOperation() { var fileSystemDialog = BuildFileSystemDialog(); - var keyGenerator = new KeyGenerator(context.AppConfig, ModuleLogger(nameof(KeyGenerator)), context.Settings); + var keyGenerator = new KeyGenerator(context.AppConfig, integrityModule, ModuleLogger(nameof(KeyGenerator)), context.Settings); var moduleLogger = ModuleLogger(nameof(BrowserApplication)); var browser = new BrowserApplication( context.AppConfig, diff --git a/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs b/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs new file mode 100644 index 00000000..2c4b13e2 --- /dev/null +++ b/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +namespace SafeExamBrowser.Configuration.Contracts.Integrity +{ + /// + /// Provides functionality related to application integrity. + /// + public interface IIntegrityModule + { + /// + /// Attempts to calculate the browser exam key. + /// + bool TryCalculateBrowserExamKey(string configurationKey, string salt, out string browserExamKey); + + /// + /// Attempts to verify the code signature. + /// + bool TryVerifyCodeSignature(out bool isValid); + } +} diff --git a/SafeExamBrowser.Configuration.Contracts/SafeExamBrowser.Configuration.Contracts.csproj b/SafeExamBrowser.Configuration.Contracts/SafeExamBrowser.Configuration.Contracts.csproj index 4717e9bb..d2fc30a2 100644 --- a/SafeExamBrowser.Configuration.Contracts/SafeExamBrowser.Configuration.Contracts.csproj +++ b/SafeExamBrowser.Configuration.Contracts/SafeExamBrowser.Configuration.Contracts.csproj @@ -59,6 +59,7 @@ + diff --git a/SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs b/SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs index 457dcda5..a103d1ee 100644 --- a/SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs +++ b/SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs @@ -11,6 +11,7 @@ using System.Security.Cryptography; using System.Text; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; +using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Settings; @@ -20,15 +21,17 @@ namespace SafeExamBrowser.Configuration.Cryptography { private readonly SHA256Managed algorithm; private readonly AppConfig appConfig; + private readonly IIntegrityModule integrityModule; private readonly ILogger logger; private readonly AppSettings settings; private string browserExamKey; - public KeyGenerator(AppConfig appConfig, ILogger logger, AppSettings settings) + public KeyGenerator(AppConfig appConfig, IIntegrityModule integrityModule, ILogger logger, AppSettings settings) { this.algorithm = new SHA256Managed(); this.appConfig = appConfig; + this.integrityModule = integrityModule; this.logger = logger; this.settings = settings; } @@ -37,7 +40,7 @@ namespace SafeExamBrowser.Configuration.Cryptography { var urlWithoutFragment = url.Split('#')[0]; var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey()))); - var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); + var key = ToString(hash); return key; } @@ -46,7 +49,7 @@ namespace SafeExamBrowser.Configuration.Cryptography { var urlWithoutFragment = url.Split('#')[0]; var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.Browser.ConfigurationKey)); - var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); + var key = ToString(hash); return key; } @@ -55,21 +58,35 @@ namespace SafeExamBrowser.Configuration.Cryptography { var salt = settings.Browser.BrowserExamKeySalt; - if (salt == default(byte[])) + if (salt == default || salt.Length == 0) { salt = new byte[0]; logger.Warn("The current configuration does not contain a salt value for the browser exam key!"); } - using (var algorithm = new HMACSHA256(salt)) + if (integrityModule.TryCalculateBrowserExamKey(settings.Browser.ConfigurationKey, ToString(salt), out browserExamKey)) { - var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + settings.Browser.ConfigurationKey)); - var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty); - - browserExamKey = key; - - return browserExamKey; + logger.Debug("Successfully calculated BEK using integrity module."); } + else + { + logger.Warn("Failed to calculate BEK using integrity module! Falling back to simplified calculation..."); + + using (var algorithm = new HMACSHA256(salt)) + { + var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + settings.Browser.ConfigurationKey)); + var key = ToString(hash); + + browserExamKey = key; + } + } + + return browserExamKey; + } + + private string ToString(byte[] bytes) + { + return BitConverter.ToString(bytes).ToLower().Replace("-", string.Empty); } } } diff --git a/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs b/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs new file mode 100644 index 00000000..c8d15f0d --- /dev/null +++ b/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Runtime.InteropServices; +using SafeExamBrowser.Configuration.Contracts.Integrity; +using SafeExamBrowser.Logging.Contracts; + +namespace SafeExamBrowser.Configuration.Integrity +{ + public class IntegrityModule : IIntegrityModule + { + const string DLL_NAME = +#if X86 + "seb_x86.dll"; +#else + "seb_x64.dll"; +#endif + + private readonly ILogger logger; + + public IntegrityModule(ILogger logger) + { + this.logger = logger; + } + + public bool TryCalculateBrowserExamKey(string configurationKey, string salt, out string browserExamKey) + { + browserExamKey = default; + + try + { + browserExamKey = CalculateBrowserExamKey(configurationKey, salt); + } + catch (DllNotFoundException) + { + logger.Warn("Integrity module is not present!"); + } + catch (Exception e) + { + logger.Error("Unexpected error while attempting to calculate browser exam key!", e); + } + + return browserExamKey != default; + } + + public bool TryVerifyCodeSignature(out bool isValid) + { + var success = false; + + isValid = default; + + try + { + isValid = VerifyCodeSignature(); + success = true; + } + catch (DllNotFoundException) + { + logger.Warn("Integrity module is not present!"); + } + catch (Exception e) + { + logger.Error("Unexpected error while attempting to verify code signature!", e); + } + + return success; + } + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.BStr)] + private static extern string CalculateBrowserExamKey(string configurationKey, string salt); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + private static extern bool VerifyCodeSignature(); + } +} diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index ce86a006..1df31067 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -16,7 +16,7 @@ true bin\x86\Debug\ - DEBUG;TRACE + TRACE;DEBUG;X86 full x86 prompt @@ -24,7 +24,7 @@ bin\x86\Release\ - TRACE + TRACE;X86 true pdbonly x86 @@ -74,6 +74,7 @@ + diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 88b97412..25ea9652 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -65,11 +65,12 @@ namespace SafeExamBrowser.I18n.Contracts LockScreen_DisplayConfigurationContinueOption, LockScreen_DisplayConfigurationTerminateOption, LockScreen_DisplayConfigurationMessage, + LockScreen_IntegrityMessage, + LockScreen_Title, + LockScreen_UnlockButton, LockScreen_UserSessionContinueOption, LockScreen_UserSessionMessage, LockScreen_UserSessionTerminateOption, - LockScreen_Title, - LockScreen_UnlockButton, LogWindow_AlwaysOnTop, LogWindow_AutoScroll, LogWindow_Title, @@ -193,6 +194,7 @@ namespace SafeExamBrowser.I18n.Contracts OperationStatus_ValidateDisplayConfiguration, OperationStatus_ValidateRemoteSessionPolicy, OperationStatus_ValidateVirtualMachinePolicy, + OperationStatus_VerifyApplicationIntegrity, OperationStatus_WaitDisclaimerConfirmation, OperationStatus_WaitExplorerStartup, OperationStatus_WaitExplorerTermination, diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml index 048c31d9..4dad7a0c 100644 --- a/SafeExamBrowser.I18n/Data/de.xml +++ b/SafeExamBrowser.I18n/Data/de.xml @@ -153,6 +153,9 @@ Eine verbotene Bildschirm-Konfiguration wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren. + + Sie verwenden eine inoffizielle SEB-Version! Bitte stellen Sie sicher, dass Sie einen offiziellen Safe Exam Browser verwenden. Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren. + SEB GESPERRT @@ -537,6 +540,9 @@ Validiere Richtlinie für virtuelle Maschinen + + Überprüfe Integrität + Warte auf die Bestätigung des Hinweises zur Fernüberwachung diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml index 99604379..f08690c4 100644 --- a/SafeExamBrowser.I18n/Data/en.xml +++ b/SafeExamBrowser.I18n/Data/en.xml @@ -153,6 +153,9 @@ A prohibited display configuration has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password. + + You are using an unofficial SEB version! Please make sure to use an official Safe Exam Browser. In order to unlock SEB, please enter the correct unlock password. + SEB LOCKED @@ -537,6 +540,9 @@ Validating virtual machine policy + + Verifying integrity + Waiting for confirmation of the disclaimer diff --git a/SafeExamBrowser.I18n/Data/fr.xml b/SafeExamBrowser.I18n/Data/fr.xml index ea2fcb68..4fb51cc1 100644 --- a/SafeExamBrowser.I18n/Data/fr.xml +++ b/SafeExamBrowser.I18n/Data/fr.xml @@ -153,6 +153,9 @@ Une configuration d'affichage interdite a été détectée. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct. + + Vous utilisez une version non officielle de SEB! Assurez-vous d'utiliser un Safe Exam Browser officiel. Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct. + SEB VEROUILLE @@ -537,6 +540,9 @@ Validation de la directive sur les machines virtuelles + + Vérification de l'intégrité + En attente de confirmation de la clause de non-responsabilité diff --git a/SafeExamBrowser.I18n/Data/it.xml b/SafeExamBrowser.I18n/Data/it.xml index 3a080e4a..d290f404 100644 --- a/SafeExamBrowser.I18n/Data/it.xml +++ b/SafeExamBrowser.I18n/Data/it.xml @@ -153,6 +153,9 @@ È stata rilevata una configurazione di visualizzazione vietata. Per sbloccare SEB, seleziona una delle opzioni disponibili e inserisci la password di sblocco corretta. + + Stai usando una versione SEB non ufficiale! Assicurati di utilizzare un Safe Exam Browser ufficiale. Per sbloccare SEB, inserisci la password di sblocco corretta. + SEB BLOCCATO @@ -537,6 +540,9 @@ Convalida dei criteri della macchina virtuale + + Verifica dell'integrità + In attesa di conferma del disclaimer diff --git a/SafeExamBrowser.I18n/Data/zh.xml b/SafeExamBrowser.I18n/Data/zh.xml index cc0e9580..a90dd312 100644 --- a/SafeExamBrowser.I18n/Data/zh.xml +++ b/SafeExamBrowser.I18n/Data/zh.xml @@ -138,6 +138,9 @@ 检测到禁止的显示配置。 要解锁 SEB,请选择可用选项之一并输入正确的解锁密码。 + + 您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB,请输入正确的解锁密码。 + 防作弊考试专用浏览器已锁定 @@ -492,6 +495,9 @@ 验证虚拟机策略 + + 验证完整性 + 等待确认免责声明 diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index c741bf8b..b9701929 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -17,6 +17,7 @@ using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Configuration.DataCompression; using SafeExamBrowser.Configuration.DataFormats; using SafeExamBrowser.Configuration.DataResources; +using SafeExamBrowser.Configuration.Integrity; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.OperationModel; using SafeExamBrowser.Core.Operations; @@ -63,6 +64,7 @@ namespace SafeExamBrowser.Runtime var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); var args = Environment.GetCommandLineArgs(); + var integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule))); var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory))); var desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor))); var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo); @@ -85,6 +87,7 @@ namespace SafeExamBrowser.Runtime bootstrapOperations.Enqueue(new I18nOperation(logger, text)); bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger)); + bootstrapOperations.Enqueue(new IntegrityOperation(integrityModule, logger)); sessionOperations.Enqueue(new SessionInitializationOperation(configuration, fileSystem, logger, runtimeHost, sessionContext)); sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, new FileSystem(), new HashAlgorithm(), logger, sessionContext)); diff --git a/SafeExamBrowser.Runtime/Operations/IntegrityOperation.cs b/SafeExamBrowser.Runtime/Operations/IntegrityOperation.cs new file mode 100644 index 00000000..f5583308 --- /dev/null +++ b/SafeExamBrowser.Runtime/Operations/IntegrityOperation.cs @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Configuration.Contracts.Integrity; +using SafeExamBrowser.Core.Contracts.OperationModel; +using SafeExamBrowser.Core.Contracts.OperationModel.Events; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.Logging.Contracts; + +namespace SafeExamBrowser.Runtime.Operations +{ + internal class IntegrityOperation : IOperation + { + private readonly IIntegrityModule module; + private readonly ILogger logger; + + public event ActionRequiredEventHandler ActionRequired { add { } remove { } } + public event StatusChangedEventHandler StatusChanged; + + public IntegrityOperation(IIntegrityModule module, ILogger logger) + { + this.module = module; + this.logger = logger; + } + + public OperationResult Perform() + { + logger.Info($"Attempting to verify application integrity..."); + StatusChanged?.Invoke(TextKey.OperationStatus_VerifyApplicationIntegrity); + + if (module.TryVerifyCodeSignature(out var isValid)) + { + if (isValid) + { + logger.Info("Application integrity successfully verified."); + } + else + { + logger.Warn("Application integrity is compromised!"); + } + } + else + { + logger.Warn("Failed to verify application integrity!"); + } + + return OperationResult.Success; + } + + public OperationResult Revert() + { + return OperationResult.Success; + } + } +} diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj index ed75cfff..8d981d58 100644 --- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj +++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj @@ -106,6 +106,7 @@ +