diff --git a/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashEncryptionTests.cs b/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyEncryptionTests.cs similarity index 94% rename from SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashEncryptionTests.cs rename to SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyEncryptionTests.cs index 783f69d4..942d031c 100644 --- a/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashEncryptionTests.cs +++ b/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyEncryptionTests.cs @@ -21,13 +21,13 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Configuration.UnitTests.Cryptography { [TestClass] - public class PublicKeyHashEncryptionTests + public class PublicKeyEncryptionTests { private Mock logger; private Mock store; private X509Certificate2 certificate; - private PublicKeyHashEncryption sut; + private PublicKeyEncryption sut; [TestInitialize] public void Initialize() @@ -38,7 +38,7 @@ namespace SafeExamBrowser.Configuration.UnitTests.Cryptography LoadCertificate(); store.Setup(s => s.TryGetCertificateWith(It.IsAny(), out certificate)).Returns(true); - sut = new PublicKeyHashEncryption(store.Object, logger.Object); + sut = new PublicKeyEncryption(store.Object, logger.Object); } [TestMethod] diff --git a/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashWithSymmetricKeyEncryptionTests.cs b/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeySymmetricEncryptionTests.cs similarity index 92% rename from SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashWithSymmetricKeyEncryptionTests.cs rename to SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeySymmetricEncryptionTests.cs index 61f28412..7322535d 100644 --- a/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeyHashWithSymmetricKeyEncryptionTests.cs +++ b/SafeExamBrowser.Configuration.UnitTests/Cryptography/PublicKeySymmetricEncryptionTests.cs @@ -21,13 +21,13 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Configuration.UnitTests.Cryptography { [TestClass] - public class PublicKeyHashWithSymmetricKeyEncryptionTests + public class PublicKeySymmetricEncryptionTests { private Mock logger; private PasswordEncryption passwordEncryption; private Mock store; - private PublicKeyHashWithSymmetricKeyEncryption sut; + private PublicKeySymmetricEncryption sut; private X509Certificate2 certificate; [TestInitialize] @@ -40,7 +40,7 @@ namespace SafeExamBrowser.Configuration.UnitTests.Cryptography LoadCertificate(); store.Setup(s => s.TryGetCertificateWith(It.IsAny(), out certificate)).Returns(true); - sut = new PublicKeyHashWithSymmetricKeyEncryption(store.Object, logger.Object, passwordEncryption); + sut = new PublicKeySymmetricEncryption(store.Object, logger.Object, passwordEncryption); } [TestMethod] diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinaryParserTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinaryParserTests.cs new file mode 100644 index 00000000..12082f58 --- /dev/null +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinaryParserTests.cs @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2019 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.IO; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Configuration.DataFormats; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Configuration.Cryptography; +using SafeExamBrowser.Contracts.Configuration.DataCompression; +using SafeExamBrowser.Contracts.Configuration.DataFormats; +using SafeExamBrowser.Contracts.Logging; + +namespace SafeExamBrowser.Configuration.UnitTests.DataFormats +{ + [TestClass] + public class BinaryParserTests + { + private Mock compressor; + private Mock hashAlgorithm; + private Mock logger; + private Mock passwordEncryption; + private Mock publicKeyEncryption; + private Mock symmetricEncryption; + private Mock xmlParser; + + private BinaryParser sut; + + [TestInitialize] + public void Initialize() + { + compressor = new Mock(); + hashAlgorithm = new Mock(); + logger = new Mock(); + passwordEncryption = new Mock(); + publicKeyEncryption = new Mock(); + symmetricEncryption = new Mock(); + xmlParser = new Mock(); + + xmlParser.Setup(p => p.TryParse(It.IsAny(), It.IsAny())).Returns(new ParseResult { Status = LoadStatus.Success }); + + sut = new BinaryParser(compressor.Object, hashAlgorithm.Object, logger.Object, passwordEncryption.Object, publicKeyEncryption.Object, symmetricEncryption.Object, xmlParser.Object); + } + + [TestMethod] + public void MustCorrectlyDetectValidPrefixes() + { + var data = new byte[123]; + var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()); + var pwcc = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PasswordConfigureClient).Concat(data).ToArray()); + var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray()); + var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray()); + var phsk = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKeySymmetric).Concat(data).ToArray()); + + Assert.IsFalse(sut.CanParse(null)); + Assert.IsFalse(sut.CanParse(new MemoryStream())); + Assert.IsFalse(sut.CanParse(new MemoryStream(data))); + + Assert.IsTrue(sut.CanParse(pswd)); + Assert.IsTrue(sut.CanParse(pwcc)); + Assert.IsTrue(sut.CanParse(plnd)); + Assert.IsTrue(sut.CanParse(pkhs)); + Assert.IsTrue(sut.CanParse(phsk)); + } + + [TestMethod] + public void MustCorrectlyParsePasswordBlock() + { + var data = new byte[123]; + var decrypted = default(Stream); + var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream; + + passwordEncryption.Setup(p => p.Decrypt(It.IsAny(), It.Is(s => s == "wrong"), out decrypted)).Returns(LoadStatus.PasswordNeeded); + passwordEncryption.Setup(p => p.Decrypt(It.IsAny(), It.Is(s => s == "correct"), out decrypted)).Returns(LoadStatus.Success); + + var result = sut.TryParse(pswd); + + Assert.AreEqual(LoadStatus.PasswordNeeded, result.Status); + + result = sut.TryParse(pswd, new PasswordParameters { Password = "wrong" }); + + Assert.AreEqual(LoadStatus.PasswordNeeded, result.Status); + + result = sut.TryParse(pswd, new PasswordParameters { Password = "correct" }); + + passwordEncryption.Verify(p => p.Decrypt(It.IsAny(), It.IsAny(), out decrypted), Times.AtLeastOnce); + xmlParser.Verify(p => p.TryParse(It.Is(s => s == decrypted), It.IsAny()), Times.Once); + publicKeyEncryption.VerifyNoOtherCalls(); + symmetricEncryption.VerifyNoOtherCalls(); + } + + [TestMethod] + public void MustCorrectlyParsePlainDataBlock() + { + var data = new byte[123]; + var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray()); + + compressor.Setup(c => c.Decompress(It.IsAny())).Returns(plnd); + compressor.Setup(c => c.Peek(It.IsAny(), It.IsAny())).Returns(Encoding.UTF8.GetBytes(BinaryBlock.PlainData)); + compressor.Setup(c => c.IsCompressed(It.IsAny())).Returns(true); + + var result = sut.TryParse(plnd); + + compressor.Verify(c => c.IsCompressed(It.IsAny()), Times.AtLeastOnce); + compressor.Verify(c => c.Decompress(It.IsAny()), Times.AtLeastOnce); + xmlParser.Verify(x => x.TryParse(It.IsAny(), It.IsAny()), Times.Once); + passwordEncryption.VerifyNoOtherCalls(); + publicKeyEncryption.VerifyNoOtherCalls(); + symmetricEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(LoadStatus.Success, result.Status); + } + + [TestMethod] + public void MustCorrectlyParsePublicKeyBlock() + { + var data = new byte[123]; + var certificate = default(X509Certificate2); + var decrypted = default(Stream); + var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream; + var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray()); + + passwordEncryption.Setup(p => p.Decrypt(It.IsAny(), It.IsAny(), out decrypted)).Returns(LoadStatus.Success); + publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny(), out pswd, out certificate)).Returns(LoadStatus.Success); + + var result = sut.TryParse(pkhs, new PasswordParameters { Password = "blubb" }); + + publicKeyEncryption.Verify(p => p.Decrypt(It.IsAny(), out decrypted, out certificate), Times.Once); + passwordEncryption.Verify(p => p.Decrypt(It.IsAny(), It.Is(s => s == "blubb"), out decrypted), Times.Once); + symmetricEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(LoadStatus.Success, result.Status); + } + + [TestMethod] + public void MustOnlyParseIfFormatSupported() + { + var data = new byte[123]; + var certificate = default(X509Certificate2); + var decrypted = default(Stream); + var pswd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.Password).Concat(data).ToArray()) as Stream; + var pwcc = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PasswordConfigureClient).Concat(data).ToArray()); + var plnd = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PlainData).Concat(data).ToArray()); + var pkhs = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKey).Concat(data).ToArray()); + var phsk = new MemoryStream(Encoding.UTF8.GetBytes(BinaryBlock.PublicKeySymmetric).Concat(data).ToArray()); + + passwordEncryption.Setup(p => p.Decrypt(It.IsAny(), It.IsAny(), out decrypted)).Returns(LoadStatus.Success); + publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny(), out pswd, out certificate)).Returns(LoadStatus.Success); + symmetricEncryption.Setup(p => p.Decrypt(It.IsAny(), out pswd, out certificate)).Returns(LoadStatus.Success); + + Assert.AreEqual(LoadStatus.InvalidData, sut.TryParse(new MemoryStream(data)).Status); + + Assert.AreEqual(LoadStatus.Success, sut.TryParse(pswd, new PasswordParameters { Password = "blubb" })?.Status); + Assert.AreEqual(LoadStatus.Success, sut.TryParse(pwcc, new PasswordParameters { Password = "blubb" })?.Status); + Assert.AreEqual(LoadStatus.Success, sut.TryParse(plnd).Status); + Assert.AreEqual(LoadStatus.Success, sut.TryParse(pkhs, new PasswordParameters { Password = "blubb" }).Status); + Assert.AreEqual(LoadStatus.Success, sut.TryParse(phsk, new PasswordParameters { Password = "blubb" }).Status); + } + } +} diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs new file mode 100644 index 00000000..7f1fe6ee --- /dev/null +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 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 Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace SafeExamBrowser.Configuration.UnitTests.DataFormats +{ + [TestClass] + public class BinarySerializerTests + { + [TestInitialize] + public void Initialize() + { + + } + } +} diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs new file mode 100644 index 00000000..64035e30 --- /dev/null +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 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 Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace SafeExamBrowser.Configuration.UnitTests.DataFormats +{ + [TestClass] + public class XmlParserTests + { + [TestInitialize] + public void Initialize() + { + + } + } +} diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlSerializerTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlSerializerTests.cs new file mode 100644 index 00000000..bcd77d5a --- /dev/null +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlSerializerTests.cs @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 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 Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace SafeExamBrowser.Configuration.UnitTests.DataFormats +{ + [TestClass] + public class XmlSerializerTests + { + [TestInitialize] + public void Initialize() + { + + } + } +} diff --git a/SafeExamBrowser.Configuration.UnitTests/SafeExamBrowser.Configuration.UnitTests.csproj b/SafeExamBrowser.Configuration.UnitTests/SafeExamBrowser.Configuration.UnitTests.csproj index 3e84475d..735d5689 100644 --- a/SafeExamBrowser.Configuration.UnitTests/SafeExamBrowser.Configuration.UnitTests.csproj +++ b/SafeExamBrowser.Configuration.UnitTests/SafeExamBrowser.Configuration.UnitTests.csproj @@ -86,9 +86,13 @@ - - + + + + + + diff --git a/SafeExamBrowser.Configuration/Cryptography/CertificateStore.cs b/SafeExamBrowser.Configuration/Cryptography/CertificateStore.cs index f3860a4a..52a7070e 100644 --- a/SafeExamBrowser.Configuration/Cryptography/CertificateStore.cs +++ b/SafeExamBrowser.Configuration/Cryptography/CertificateStore.cs @@ -13,7 +13,7 @@ using SafeExamBrowser.Contracts.Configuration.Cryptography; namespace SafeExamBrowser.Configuration.Cryptography { - internal class CertificateStore : ICertificateStore + public class CertificateStore : ICertificateStore { private readonly X509Store[] stores = new[] { diff --git a/SafeExamBrowser.Configuration/Cryptography/PasswordEncryption.cs b/SafeExamBrowser.Configuration/Cryptography/PasswordEncryption.cs index f05082bf..14cc8e64 100644 --- a/SafeExamBrowser.Configuration/Cryptography/PasswordEncryption.cs +++ b/SafeExamBrowser.Configuration/Cryptography/PasswordEncryption.cs @@ -10,11 +10,12 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Configuration.Cryptography; using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Configuration.Cryptography { - internal class PasswordEncryption + public class PasswordEncryption : IPasswordEncryption { private const int BLOCK_SIZE = 16; private const int HEADER_SIZE = 2; @@ -26,14 +27,14 @@ namespace SafeExamBrowser.Configuration.Cryptography private ILogger logger; - internal PasswordEncryption(ILogger logger) + public PasswordEncryption(ILogger logger) { this.logger = logger; } - internal LoadStatus Decrypt(Stream data, string password, out Stream decryptedData) + public LoadStatus Decrypt(Stream data, string password, out Stream decrypted) { - decryptedData = default(Stream); + decrypted = default(Stream); if (password == null) { @@ -49,17 +50,17 @@ namespace SafeExamBrowser.Configuration.Cryptography return FailForInvalidHmac(); } - decryptedData = Decrypt(data, encryptionKey, originalHmac.Length); + decrypted = Decrypt(data, encryptionKey, originalHmac.Length); return LoadStatus.Success; } - internal SaveStatus Encrypt(Stream data, string password, out Stream encryptedData) + public SaveStatus Encrypt(Stream data, string password, out Stream encrypted) { var (authKey, authSalt, encrKey, encrSalt) = GenerateKeysForEncryption(password); - encryptedData = Encrypt(data, encrKey, out var initVector); - encryptedData = WriteEncryptionParameters(authKey, authSalt, encrSalt, initVector, encryptedData); + encrypted = Encrypt(data, encrKey, out var initVector); + encrypted = WriteEncryptionParameters(authKey, authSalt, encrSalt, initVector, encrypted); return SaveStatus.Success; } diff --git a/SafeExamBrowser.Configuration/Cryptography/PublicKeyHashEncryption.cs b/SafeExamBrowser.Configuration/Cryptography/PublicKeyEncryption.cs similarity index 93% rename from SafeExamBrowser.Configuration/Cryptography/PublicKeyHashEncryption.cs rename to SafeExamBrowser.Configuration/Cryptography/PublicKeyEncryption.cs index a3407c62..45658911 100644 --- a/SafeExamBrowser.Configuration/Cryptography/PublicKeyHashEncryption.cs +++ b/SafeExamBrowser.Configuration/Cryptography/PublicKeyEncryption.cs @@ -15,20 +15,20 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Configuration.Cryptography { - internal class PublicKeyHashEncryption + public class PublicKeyEncryption : IPublicKeyEncryption { protected const int PUBLIC_KEY_HASH_SIZE = 20; protected ICertificateStore store; protected ILogger logger; - internal PublicKeyHashEncryption(ICertificateStore store, ILogger logger) + public PublicKeyEncryption(ICertificateStore store, ILogger logger) { this.logger = logger; this.store = store; } - internal virtual LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate) + public virtual LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate) { var publicKeyHash = ParsePublicKeyHash(data); var found = store.TryGetCertificateWith(publicKeyHash, out certificate); @@ -45,7 +45,7 @@ namespace SafeExamBrowser.Configuration.Cryptography return LoadStatus.Success; } - internal virtual SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData) + public virtual SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData) { var publicKeyHash = GeneratePublicKeyHash(certificate); diff --git a/SafeExamBrowser.Configuration/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs b/SafeExamBrowser.Configuration/Cryptography/PublicKeySymmetricEncryption.cs similarity index 89% rename from SafeExamBrowser.Configuration/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs rename to SafeExamBrowser.Configuration/Cryptography/PublicKeySymmetricEncryption.cs index 71faa9f7..9d71b257 100644 --- a/SafeExamBrowser.Configuration/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs +++ b/SafeExamBrowser.Configuration/Cryptography/PublicKeySymmetricEncryption.cs @@ -16,19 +16,19 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Configuration.Cryptography { - internal class PublicKeyHashWithSymmetricKeyEncryption : PublicKeyHashEncryption + public class PublicKeySymmetricEncryption : PublicKeyEncryption { private const int ENCRYPTION_KEY_LENGTH = 32; private const int KEY_LENGTH_SIZE = 4; private PasswordEncryption passwordEncryption; - internal PublicKeyHashWithSymmetricKeyEncryption(ICertificateStore store, ILogger logger, PasswordEncryption passwordEncryption) : base(store, logger) + public PublicKeySymmetricEncryption(ICertificateStore store, ILogger logger, PasswordEncryption passwordEncryption) : base(store, logger) { this.passwordEncryption = passwordEncryption; } - internal override LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate) + public override LoadStatus Decrypt(Stream data, out Stream decryptedData, out X509Certificate2 certificate) { var publicKeyHash = ParsePublicKeyHash(data); var found = store.TryGetCertificateWith(publicKeyHash, out certificate); @@ -47,7 +47,7 @@ namespace SafeExamBrowser.Configuration.Cryptography return status; } - internal override SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData) + public override SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encryptedData) { var publicKeyHash = GeneratePublicKeyHash(certificate); var symmetricKey = GenerateSymmetricKey(); diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryBlock.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryBlock.cs index e676e0b9..e1e9196a 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinaryBlock.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinaryBlock.cs @@ -13,7 +13,7 @@ namespace SafeExamBrowser.Configuration.DataFormats internal const string Password = "pswd"; internal const string PasswordConfigureClient = "pwcc"; internal const string PlainData = "plnd"; - internal const string PublicKeyHash = "pkhs"; - internal const string PublicKeyHashWithSymmetricKey = "phsk"; + internal const string PublicKey = "pkhs"; + internal const string PublicKeySymmetric = "phsk"; } } diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs index b7af518e..70faf557 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs @@ -11,7 +11,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Cryptography; using SafeExamBrowser.Contracts.Configuration.DataCompression; @@ -26,13 +25,28 @@ namespace SafeExamBrowser.Configuration.DataFormats private IDataCompressor compressor; private IHashAlgorithm hashAlgorithm; - private IModuleLogger logger; + private ILogger logger; + private IPasswordEncryption passwordEncryption; + private IPublicKeyEncryption publicKeyEncryption; + private IPublicKeyEncryption publicKeySymmetricEncryption; + private readonly IDataParser xmlParser; - public BinaryParser(IDataCompressor compressor, IHashAlgorithm hashAlgorithm, IModuleLogger logger) + public BinaryParser( + IDataCompressor compressor, + IHashAlgorithm hashAlgorithm, + ILogger logger, + IPasswordEncryption passwordEncryption, + IPublicKeyEncryption publicKeyEncryption, + IPublicKeyEncryption publicKeySymmetricEncryption, + IDataParser xmlParser) { this.compressor = compressor; this.hashAlgorithm = hashAlgorithm; this.logger = logger; + this.passwordEncryption = passwordEncryption; + this.publicKeyEncryption = publicKeyEncryption; + this.publicKeySymmetricEncryption = publicKeySymmetricEncryption; + this.xmlParser = xmlParser; } public bool CanParse(Stream data) @@ -55,7 +69,7 @@ namespace SafeExamBrowser.Configuration.DataFormats } catch (Exception e) { - logger.Error($"Failed to determine whether '{data}' with {data.Length / 1000.0} KB data matches the {FormatType.Binary} format!", e); + logger.Error($"Failed to determine whether '{data}' with {data?.Length / 1000.0} KB data matches the {FormatType.Binary} format!", e); } return false; @@ -78,9 +92,9 @@ namespace SafeExamBrowser.Configuration.DataFormats return ParsePasswordBlock(data, prefix, password); case BinaryBlock.PlainData: return ParsePlainDataBlock(data); - case BinaryBlock.PublicKeyHash: - case BinaryBlock.PublicKeyHashWithSymmetricKey: - return ParsePublicKeyHashBlock(data, prefix, password); + case BinaryBlock.PublicKey: + case BinaryBlock.PublicKeySymmetric: + return ParsePublicKeyBlock(data, prefix, password); } } @@ -91,20 +105,19 @@ namespace SafeExamBrowser.Configuration.DataFormats private ParseResult ParsePasswordBlock(Stream data, string prefix, PasswordParameters password = null) { - var encryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption))); var result = new ParseResult(); if (password != null) { - var encryptionParameters = DetermineEncryptionParametersFor(prefix, password); + var parameters = DetermineEncryptionParametersFor(prefix, password); logger.Debug($"Attempting to parse password block with prefix '{prefix}'..."); - result.Status = encryption.Decrypt(data, encryptionParameters.Password, out var decrypted); + result.Status = passwordEncryption.Decrypt(data, parameters.Password, out var decrypted); if (result.Status == LoadStatus.Success) { result = ParsePlainDataBlock(decrypted); - result.Encryption = encryptionParameters; + result.Encryption = parameters; } } else @@ -117,20 +130,18 @@ namespace SafeExamBrowser.Configuration.DataFormats private ParseResult ParsePlainDataBlock(Stream data) { - var xmlFormat = new XmlParser(logger.CloneFor(nameof(XmlParser))); - data = compressor.IsCompressed(data) ? compressor.Decompress(data) : data; logger.Debug("Attempting to parse plain data block..."); - var result = xmlFormat.TryParse(data); + var result = xmlParser.TryParse(data); result.Format = FormatType.Binary; return result; } - private ParseResult ParsePublicKeyHashBlock(Stream data, string prefix, PasswordParameters password = null) + private ParseResult ParsePublicKeyBlock(Stream data, string prefix, PasswordParameters password = null) { - var encryption = DetermineEncryptionForPublicKeyHashBlock(prefix); + var encryption = prefix == BinaryBlock.PublicKey ? publicKeyEncryption : publicKeySymmetricEncryption; var result = new ParseResult(); logger.Debug($"Attempting to parse public key hash block with prefix '{prefix}'..."); @@ -139,29 +150,17 @@ namespace SafeExamBrowser.Configuration.DataFormats if (result.Status == LoadStatus.Success) { result = TryParse(decrypted, password); - result.Encryption = new PublicKeyHashParameters + result.Encryption = new PublicKeyParameters { Certificate = certificate, InnerEncryption = result.Encryption as PasswordParameters, - SymmetricEncryption = prefix == BinaryBlock.PublicKeyHashWithSymmetricKey + SymmetricEncryption = prefix == BinaryBlock.PublicKeySymmetric }; } return result; } - private PublicKeyHashEncryption DetermineEncryptionForPublicKeyHashBlock(string prefix) - { - var passwordEncryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption))); - - if (prefix == BinaryBlock.PublicKeyHash) - { - return new PublicKeyHashEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyHashEncryption))); - } - - return new PublicKeyHashWithSymmetricKeyEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyHashWithSymmetricKeyEncryption)), passwordEncryption); - } - private PasswordParameters DetermineEncryptionParametersFor(string prefix, PasswordParameters password) { var parameters = new PasswordParameters(); diff --git a/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs b/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs index ecf43047..28d69594 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs @@ -43,7 +43,7 @@ namespace SafeExamBrowser.Configuration.DataFormats case PasswordParameters p: result = SerializePasswordBlock(data, p); break; - case PublicKeyHashParameters p: + case PublicKeyParameters p: result = SerializePublicKeyHashBlock(data, p); break; default: @@ -103,14 +103,14 @@ namespace SafeExamBrowser.Configuration.DataFormats return result; } - private SerializeResult SerializePublicKeyHashBlock(IDictionary data, PublicKeyHashParameters parameters) + private SerializeResult SerializePublicKeyHashBlock(IDictionary data, PublicKeyParameters parameters) { var result = SerializePublicKeyHashInnerBlock(data, parameters); if (result.Status == SaveStatus.Success) { var encryption = DetermineEncryptionForPublicKeyHashBlock(parameters); - var prefix = parameters.SymmetricEncryption ? BinaryBlock.PublicKeyHashWithSymmetricKey : BinaryBlock.PublicKeyHash; + var prefix = parameters.SymmetricEncryption ? BinaryBlock.PublicKeySymmetric : BinaryBlock.PublicKey; logger.Debug("Attempting to serialize public key hash block..."); @@ -125,7 +125,7 @@ namespace SafeExamBrowser.Configuration.DataFormats return result; } - private SerializeResult SerializePublicKeyHashInnerBlock(IDictionary data, PublicKeyHashParameters parameters) + private SerializeResult SerializePublicKeyHashInnerBlock(IDictionary data, PublicKeyParameters parameters) { if (parameters.InnerEncryption is PasswordParameters password) { @@ -135,16 +135,16 @@ namespace SafeExamBrowser.Configuration.DataFormats return SerializePlainDataBlock(data, true); } - private PublicKeyHashEncryption DetermineEncryptionForPublicKeyHashBlock(PublicKeyHashParameters parameters) + private PublicKeyEncryption DetermineEncryptionForPublicKeyHashBlock(PublicKeyParameters parameters) { var passwordEncryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption))); if (parameters.SymmetricEncryption) { - return new PublicKeyHashWithSymmetricKeyEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyHashWithSymmetricKeyEncryption)), passwordEncryption); + return new PublicKeySymmetricEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeySymmetricEncryption)), passwordEncryption); } - return new PublicKeyHashEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyHashEncryption))); + return new PublicKeyEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyEncryption))); } private Stream WritePrefix(string prefix, Stream data) diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index bb0ba43b..f52d3191 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -64,8 +64,8 @@ - - + + diff --git a/SafeExamBrowser.Contracts/Configuration/Cryptography/IPasswordEncryption.cs b/SafeExamBrowser.Contracts/Configuration/Cryptography/IPasswordEncryption.cs new file mode 100644 index 00000000..f7f7464d --- /dev/null +++ b/SafeExamBrowser.Contracts/Configuration/Cryptography/IPasswordEncryption.cs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019 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.IO; + +namespace SafeExamBrowser.Contracts.Configuration.Cryptography +{ + /// + /// Encrypts and decrypts data with a password. + /// + public interface IPasswordEncryption + { + /// + /// Attempts to decrypt the given data. The decrypted data stream can only be considered valid if + /// is returned! + /// + LoadStatus Decrypt(Stream data, string password, out Stream decrypted); + + /// + /// Attempts to encrypt the given data. The encrypted data stream can only be considered valid if + /// is returned. + /// + SaveStatus Encrypt(Stream data, string password, out Stream encrypted); + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/Cryptography/IPublicKeyEncryption.cs b/SafeExamBrowser.Contracts/Configuration/Cryptography/IPublicKeyEncryption.cs new file mode 100644 index 00000000..906ab9a0 --- /dev/null +++ b/SafeExamBrowser.Contracts/Configuration/Cryptography/IPublicKeyEncryption.cs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 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.IO; +using System.Security.Cryptography.X509Certificates; + +namespace SafeExamBrowser.Contracts.Configuration.Cryptography +{ + /// + /// Encrypts and decrypts data with a certificate. + /// + public interface IPublicKeyEncryption + { + /// + /// Attempts to decrypt the given data. The decrypted data stream and the certificate can only be considered valid if + /// is returned! + /// + LoadStatus Decrypt(Stream data, out Stream decrypted, out X509Certificate2 certificate); + + /// + /// Attempts to encrypt the given data. The encrypted data stream can only be considered valid if + /// is returned. + /// + SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encrypted); + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyHashParameters.cs b/SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyParameters.cs similarity index 94% rename from SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyHashParameters.cs rename to SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyParameters.cs index f1b1ff2a..5f944325 100644 --- a/SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyHashParameters.cs +++ b/SafeExamBrowser.Contracts/Configuration/Cryptography/PublicKeyParameters.cs @@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.Configuration.Cryptography /// /// Holds all parameters for data encryption by certificate. /// - public class PublicKeyHashParameters : EncryptionParameters + public class PublicKeyParameters : EncryptionParameters { /// /// The certificate holding the public key used for encryption. diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 2892e1cc..c55f92f3 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -59,8 +59,10 @@ + + - + diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 8fc2a894..50605809 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -62,12 +62,12 @@ namespace SafeExamBrowser.Runtime InitializeText(); var messageBox = new MessageBox(text); - var desktopFactory = new DesktopFactory(new ModuleLogger(logger, nameof(DesktopFactory))); - var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); - var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory))); + var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory))); + var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods); + var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger); - var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), new ModuleLogger(logger, nameof(RuntimeHost)), FIVE_SECONDS); - var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ServiceProxy))); + var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), ModuleLogger(nameof(RuntimeHost)), FIVE_SECONDS); + var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy))); var sessionContext = new SessionContext(); var uiFactory = new UserInterfaceFactory(text); @@ -116,18 +116,23 @@ namespace SafeExamBrowser.Runtime var programCopyright = executable.GetCustomAttribute().Copyright; var programTitle = executable.GetCustomAttribute().Title; var programVersion = executable.GetCustomAttribute().InformationalVersion; - var compressor = new GZipCompressor(new ModuleLogger(logger, nameof(GZipCompressor))); - var repositoryLogger = new ModuleLogger(logger, nameof(ConfigurationRepository)); + var compressor = new GZipCompressor(ModuleLogger(nameof(GZipCompressor))); + var passwordEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption))); + var publicKeyEncryption = new PublicKeyEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeyEncryption))); + var symmetricInnerEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption))); + var symmetricEncryption = new PublicKeySymmetricEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeySymmetricEncryption)), symmetricInnerEncryption); + var repositoryLogger = ModuleLogger(nameof(ConfigurationRepository)); + var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser))); configuration = new ConfigurationRepository(new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion); appConfig = configuration.InitializeAppConfig(); - configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), new ModuleLogger(logger, nameof(BinaryParser)))); - configuration.Register(new BinarySerializer(compressor, new ModuleLogger(logger, nameof(BinarySerializer)))); - configuration.Register(new XmlParser(new ModuleLogger(logger, nameof(XmlParser)))); - configuration.Register(new XmlSerializer(new ModuleLogger(logger, nameof(XmlSerializer)))); - configuration.Register(new FileResourceLoader(new ModuleLogger(logger, nameof(FileResourceLoader)))); - configuration.Register(new FileResourceSaver(new ModuleLogger(logger, nameof(FileResourceSaver)))); + configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser)); + configuration.Register(new BinarySerializer(compressor, ModuleLogger(nameof(BinarySerializer)))); + configuration.Register(new XmlParser(ModuleLogger(nameof(XmlParser)))); + configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer)))); + configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader)))); + configuration.Register(new FileResourceSaver(ModuleLogger(nameof(FileResourceSaver)))); configuration.Register(new NetworkResourceLoader(appConfig, new ModuleLogger(logger, nameof(NetworkResourceLoader)))); } @@ -148,5 +153,10 @@ namespace SafeExamBrowser.Runtime text = new Text(logger); textResource = new XmlTextResource(path); } + + private IModuleLogger ModuleLogger(string moduleInfo) + { + return new ModuleLogger(logger, moduleInfo); + } } }