From 817d9eaefc6e40da0f1a0b2f57a3f08e5bd34b49 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Wed, 20 Feb 2019 08:47:30 +0100 Subject: [PATCH] SEBWIN-296: Implemented unit tests for binary serializer. --- .../DataFormats/BinarySerializerTests.cs | 131 ++++++++++++++++++ .../DataFormats/BinaryParser.cs | 8 +- .../DataFormats/BinarySerializer.cs | 37 +++-- SafeExamBrowser.Runtime/CompositionRoot.cs | 6 +- 4 files changed, 156 insertions(+), 26 deletions(-) diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs index 7f1fe6ee..91f53a94 100644 --- a/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/BinarySerializerTests.cs @@ -6,17 +6,148 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography.X509Certificates; 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 BinarySerializerTests { + private Mock compressor; + private Mock logger; + private Mock passwordEncryption; + private Mock publicKeyEncryption; + private Mock symmetricEncryption; + private Mock xmlSerializer; + private SerializeResult xmlResult; + + private BinarySerializer sut; + [TestInitialize] public void Initialize() { + compressor = new Mock(); + logger = new Mock(); + passwordEncryption = new Mock(); + publicKeyEncryption = new Mock(); + symmetricEncryption = new Mock(); + xmlResult = new SerializeResult { Data = new MemoryStream(), Status = SaveStatus.Success }; + xmlSerializer = new Mock(); + compressor.Setup(c => c.Compress(It.IsAny())).Returns(new MemoryStream()); + xmlSerializer.Setup(x => x.TrySerialize(It.IsAny>(), It.IsAny())).Returns(xmlResult); + + sut = new BinarySerializer(compressor.Object, logger.Object, passwordEncryption.Object, publicKeyEncryption.Object, symmetricEncryption.Object, xmlSerializer.Object); + } + + [TestMethod] + public void MustOnlySupportBinaryFormat() + { + var values = Enum.GetValues(typeof(FormatType)); + + foreach (var value in values) + { + if (value is FormatType format && format != FormatType.Binary) + { + Assert.IsFalse(sut.CanSerialize(format)); + } + } + + Assert.IsTrue(sut.CanSerialize(FormatType.Binary)); + } + + [TestMethod] + public void MustCorrectlySerializePlainDataBlock() + { + var data = new Dictionary(); + var result = sut.TrySerialize(data); + + compressor.Verify(c => c.Compress(It.IsAny()), Times.Exactly(2)); + xmlSerializer.Verify(x => x.TrySerialize(It.Is>(d => d == data), It.Is(e => e == null)), Times.Once); + passwordEncryption.VerifyNoOtherCalls(); + publicKeyEncryption.VerifyNoOtherCalls(); + symmetricEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(SaveStatus.Success, result.Status); + } + + [TestMethod] + public void MustCorrectlySerializePasswordBlock() + { + var encrypted = new MemoryStream() as Stream; + var data = new Dictionary(); + var encryption = new PasswordParameters { Password = "blubb" }; + + passwordEncryption.Setup(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted)).Returns(SaveStatus.Success); + + var result = sut.TrySerialize(data, encryption); + + compressor.Verify(c => c.Compress(It.IsAny()), Times.Exactly(2)); + passwordEncryption.Verify(p => p.Encrypt(It.IsAny(), It.Is(s => s == encryption.Password), out encrypted), Times.Once); + xmlSerializer.Verify(x => x.TrySerialize(It.Is>(d => d == data), It.Is(e => e == null)), Times.Once); + publicKeyEncryption.VerifyNoOtherCalls(); + symmetricEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(SaveStatus.Success, result.Status); + } + + [TestMethod] + public void MustCorrectlySerializePublicKeyBlock() + { + var encrypted = new MemoryStream() as Stream; + var data = new Dictionary(); + var encryption = new PublicKeyParameters + { + InnerEncryption = new PasswordParameters { Password = "test" }, + SymmetricEncryption = false + }; + + passwordEncryption.Setup(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted)).Returns(SaveStatus.Success); + publicKeyEncryption.Setup(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted)).Returns(SaveStatus.Success); + + var result = sut.TrySerialize(data, encryption); + + compressor.Verify(c => c.Compress(It.IsAny()), Times.Exactly(2)); + passwordEncryption.Verify(p => p.Encrypt(It.IsAny(), It.Is(s => s == encryption.InnerEncryption.Password), out encrypted), Times.Once); + publicKeyEncryption.Verify(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted), Times.Once); + xmlSerializer.Verify(x => x.TrySerialize(It.Is>(d => d == data), It.Is(e => e == null)), Times.Once); + symmetricEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(SaveStatus.Success, result.Status); + } + + [TestMethod] + public void MustCorrectlySerializePublicKeySymmetricBlock() + { + var encrypted = new MemoryStream() as Stream; + var data = new Dictionary(); + var encryption = new PublicKeyParameters + { + SymmetricEncryption = true + }; + + symmetricEncryption.Setup(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted)).Returns(SaveStatus.Success); + + var result = sut.TrySerialize(data, encryption); + + compressor.Verify(c => c.Compress(It.IsAny()), Times.Exactly(2)); + symmetricEncryption.Verify(p => p.Encrypt(It.IsAny(), It.IsAny(), out encrypted), Times.Once); + xmlSerializer.Verify(x => x.TrySerialize(It.Is>(d => d == data), It.Is(e => e == null)), Times.Once); + passwordEncryption.VerifyNoOtherCalls(); + publicKeyEncryption.VerifyNoOtherCalls(); + + Assert.AreEqual(SaveStatus.Success, result.Status); } } } diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs index 70faf557..7d278786 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs @@ -181,19 +181,19 @@ namespace SafeExamBrowser.Configuration.DataFormats private string ReadPrefix(Stream data) { - var prefixData = new byte[PREFIX_LENGTH]; + var prefix = new byte[PREFIX_LENGTH]; if (compressor.IsCompressed(data)) { - prefixData = compressor.Peek(data, PREFIX_LENGTH); + prefix = compressor.Peek(data, PREFIX_LENGTH); } else { data.Seek(0, SeekOrigin.Begin); - data.Read(prefixData, 0, PREFIX_LENGTH); + data.Read(prefix, 0, PREFIX_LENGTH); } - return Encoding.UTF8.GetString(prefixData); + return Encoding.UTF8.GetString(prefix); } private bool IsValid(string prefix) diff --git a/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs b/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs index 28d69594..f9e47156 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinarySerializer.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Cryptography; using SafeExamBrowser.Contracts.Configuration.DataCompression; @@ -21,12 +20,26 @@ namespace SafeExamBrowser.Configuration.DataFormats public class BinarySerializer : IDataSerializer { private IDataCompressor compressor; - private IModuleLogger logger; + private ILogger logger; + private IPasswordEncryption passwordEncryption; + private IPublicKeyEncryption publicKeyEncryption; + private IPublicKeyEncryption symmetricEncryption; + private IDataSerializer xmlSerializer; - public BinarySerializer(IDataCompressor compressor, IModuleLogger logger) + public BinarySerializer( + IDataCompressor compressor, + ILogger logger, + IPasswordEncryption passwordEncryption, + IPublicKeyEncryption publicKeyEncryption, + IPublicKeyEncryption symmetricEncryption, + IDataSerializer xmlSerializer) { this.compressor = compressor; this.logger = logger; + this.passwordEncryption = passwordEncryption; + this.publicKeyEncryption = publicKeyEncryption; + this.symmetricEncryption = symmetricEncryption; + this.xmlSerializer = xmlSerializer; } public bool CanSerialize(FormatType format) @@ -65,12 +78,11 @@ namespace SafeExamBrowser.Configuration.DataFormats if (result.Status == SaveStatus.Success) { - var encryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption))); var prefix = password.IsHash ? BinaryBlock.PasswordConfigureClient : BinaryBlock.Password; logger.Debug("Attempting to serialize password block..."); - var status = encryption.Encrypt(result.Data, password.Password, out var encrypted); + var status = passwordEncryption.Encrypt(result.Data, password.Password, out var encrypted); if (status == SaveStatus.Success) { @@ -87,7 +99,6 @@ namespace SafeExamBrowser.Configuration.DataFormats { logger.Debug("Attempting to serialize plain data block..."); - var xmlSerializer = new XmlSerializer(logger.CloneFor(nameof(XmlSerializer))); var result = xmlSerializer.TrySerialize(data); if (result.Status == SaveStatus.Success) @@ -109,7 +120,7 @@ namespace SafeExamBrowser.Configuration.DataFormats if (result.Status == SaveStatus.Success) { - var encryption = DetermineEncryptionForPublicKeyHashBlock(parameters); + var encryption = parameters.SymmetricEncryption ? symmetricEncryption : publicKeyEncryption; var prefix = parameters.SymmetricEncryption ? BinaryBlock.PublicKeySymmetric : BinaryBlock.PublicKey; logger.Debug("Attempting to serialize public key hash block..."); @@ -135,18 +146,6 @@ namespace SafeExamBrowser.Configuration.DataFormats return SerializePlainDataBlock(data, true); } - private PublicKeyEncryption DetermineEncryptionForPublicKeyHashBlock(PublicKeyParameters parameters) - { - var passwordEncryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption))); - - if (parameters.SymmetricEncryption) - { - return new PublicKeySymmetricEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeySymmetricEncryption)), passwordEncryption); - } - - return new PublicKeyEncryption(new CertificateStore(), logger.CloneFor(nameof(PublicKeyEncryption))); - } - private Stream WritePrefix(string prefix, Stream data) { var prefixBytes = Encoding.UTF8.GetBytes(prefix); diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 50605809..6518cf09 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -119,16 +119,16 @@ namespace SafeExamBrowser.Runtime 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 symmetricEncryption = new PublicKeySymmetricEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption); var repositoryLogger = ModuleLogger(nameof(ConfigurationRepository)); var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser))); + var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer))); configuration = new ConfigurationRepository(new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion); appConfig = configuration.InitializeAppConfig(); 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 BinarySerializer(compressor, ModuleLogger(nameof(BinarySerializer)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlSerializer)); configuration.Register(new XmlParser(ModuleLogger(nameof(XmlParser)))); configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer)))); configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader))));