SEBWIN-296: Decoupled cryptographic implementations from data formats to be able to unit test the latter.
This commit is contained in:
parent
f817f31f2d
commit
cf7d6c4d3e
20 changed files with 392 additions and 81 deletions
|
@ -21,13 +21,13 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
|
||||
{
|
||||
[TestClass]
|
||||
public class PublicKeyHashEncryptionTests
|
||||
public class PublicKeyEncryptionTests
|
||||
{
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<ICertificateStore> 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<byte[]>(), out certificate)).Returns(true);
|
||||
|
||||
sut = new PublicKeyHashEncryption(store.Object, logger.Object);
|
||||
sut = new PublicKeyEncryption(store.Object, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
|
@ -21,13 +21,13 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
|
||||
{
|
||||
[TestClass]
|
||||
public class PublicKeyHashWithSymmetricKeyEncryptionTests
|
||||
public class PublicKeySymmetricEncryptionTests
|
||||
{
|
||||
private Mock<ILogger> logger;
|
||||
private PasswordEncryption passwordEncryption;
|
||||
private Mock<ICertificateStore> 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<byte[]>(), out certificate)).Returns(true);
|
||||
|
||||
sut = new PublicKeyHashWithSymmetricKeyEncryption(store.Object, logger.Object, passwordEncryption);
|
||||
sut = new PublicKeySymmetricEncryption(store.Object, logger.Object, passwordEncryption);
|
||||
}
|
||||
|
||||
[TestMethod]
|
|
@ -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<IDataCompressor> compressor;
|
||||
private Mock<IHashAlgorithm> hashAlgorithm;
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IPasswordEncryption> passwordEncryption;
|
||||
private Mock<IPublicKeyEncryption> publicKeyEncryption;
|
||||
private Mock<IPublicKeyEncryption> symmetricEncryption;
|
||||
private Mock<IDataParser> xmlParser;
|
||||
|
||||
private BinaryParser sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
compressor = new Mock<IDataCompressor>();
|
||||
hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||
logger = new Mock<ILogger>();
|
||||
passwordEncryption = new Mock<IPasswordEncryption>();
|
||||
publicKeyEncryption = new Mock<IPublicKeyEncryption>();
|
||||
symmetricEncryption = new Mock<IPublicKeyEncryption>();
|
||||
xmlParser = new Mock<IDataParser>();
|
||||
|
||||
xmlParser.Setup(p => p.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).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<Stream>(), It.Is<string>(s => s == "wrong"), out decrypted)).Returns(LoadStatus.PasswordNeeded);
|
||||
passwordEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), It.Is<string>(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<Stream>(), It.IsAny<string>(), out decrypted), Times.AtLeastOnce);
|
||||
xmlParser.Verify(p => p.TryParse(It.Is<Stream>(s => s == decrypted), It.IsAny<PasswordParameters>()), 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<Stream>())).Returns(plnd);
|
||||
compressor.Setup(c => c.Peek(It.IsAny<Stream>(), It.IsAny<int>())).Returns(Encoding.UTF8.GetBytes(BinaryBlock.PlainData));
|
||||
compressor.Setup(c => c.IsCompressed(It.IsAny<Stream>())).Returns(true);
|
||||
|
||||
var result = sut.TryParse(plnd);
|
||||
|
||||
compressor.Verify(c => c.IsCompressed(It.IsAny<Stream>()), Times.AtLeastOnce);
|
||||
compressor.Verify(c => c.Decompress(It.IsAny<Stream>()), Times.AtLeastOnce);
|
||||
xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), 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<Stream>(), It.IsAny<string>(), out decrypted)).Returns(LoadStatus.Success);
|
||||
publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), out pswd, out certificate)).Returns(LoadStatus.Success);
|
||||
|
||||
var result = sut.TryParse(pkhs, new PasswordParameters { Password = "blubb" });
|
||||
|
||||
publicKeyEncryption.Verify(p => p.Decrypt(It.IsAny<Stream>(), out decrypted, out certificate), Times.Once);
|
||||
passwordEncryption.Verify(p => p.Decrypt(It.IsAny<Stream>(), It.Is<string>(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<Stream>(), It.IsAny<string>(), out decrypted)).Returns(LoadStatus.Success);
|
||||
publicKeyEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), out pswd, out certificate)).Returns(LoadStatus.Success);
|
||||
symmetricEncryption.Setup(p => p.Decrypt(It.IsAny<Stream>(), 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -86,9 +86,13 @@
|
|||
<Compile Include="ConfigurationRepositoryTests.cs" />
|
||||
<Compile Include="Cryptography\HashAlgorithmTests.cs" />
|
||||
<Compile Include="Cryptography\PasswordEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyHashEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyHashWithSymmetricKeyEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeySymmetricEncryptionTests.cs" />
|
||||
<Compile Include="DataCompression\GZipCompressorTests.cs" />
|
||||
<Compile Include="DataFormats\BinaryParserTests.cs" />
|
||||
<Compile Include="DataFormats\BinarySerializerTests.cs" />
|
||||
<Compile Include="DataFormats\XmlParserTests.cs" />
|
||||
<Compile Include="DataFormats\XmlSerializerTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -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[]
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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();
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<string, object> data, PublicKeyHashParameters parameters)
|
||||
private SerializeResult SerializePublicKeyHashBlock(IDictionary<string, object> 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<string, object> data, PublicKeyHashParameters parameters)
|
||||
private SerializeResult SerializePublicKeyHashInnerBlock(IDictionary<string, object> 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)
|
||||
|
|
|
@ -64,8 +64,8 @@
|
|||
<Compile Include="Cryptography\CertificateStore.cs" />
|
||||
<Compile Include="DataCompression\GZipCompressor.cs" />
|
||||
<Compile Include="Cryptography\PasswordEncryption.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyHashEncryption.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyHashWithSymmetricKeyEncryption.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyEncryption.cs" />
|
||||
<Compile Include="Cryptography\PublicKeySymmetricEncryption.cs" />
|
||||
<Compile Include="DataFormats\BinaryParser.cs" />
|
||||
<Compile Include="DataFormats\BinarySerializer.cs" />
|
||||
<Compile Include="DataFormats\BinaryBlock.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
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypts and decrypts data with a password.
|
||||
/// </summary>
|
||||
public interface IPasswordEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to decrypt the given data. The decrypted data stream can only be considered valid if <see cref="LoadStatus.Success"/>
|
||||
/// is returned!
|
||||
/// </summary>
|
||||
LoadStatus Decrypt(Stream data, string password, out Stream decrypted);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to encrypt the given data. The encrypted data stream can only be considered valid if <see cref="SaveStatus.Success"/>
|
||||
/// is returned.
|
||||
/// </summary>
|
||||
SaveStatus Encrypt(Stream data, string password, out Stream encrypted);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypts and decrypts data with a certificate.
|
||||
/// </summary>
|
||||
public interface IPublicKeyEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to decrypt the given data. The decrypted data stream and the certificate can only be considered valid if
|
||||
/// <see cref="LoadStatus.Success"/> is returned!
|
||||
/// </summary>
|
||||
LoadStatus Decrypt(Stream data, out Stream decrypted, out X509Certificate2 certificate);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to encrypt the given data. The encrypted data stream can only be considered valid if <see cref="SaveStatus.Success"/>
|
||||
/// is returned.
|
||||
/// </summary>
|
||||
SaveStatus Encrypt(Stream data, X509Certificate2 certificate, out Stream encrypted);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
|||
/// <summary>
|
||||
/// Holds all parameters for data encryption by certificate.
|
||||
/// </summary>
|
||||
public class PublicKeyHashParameters : EncryptionParameters
|
||||
public class PublicKeyParameters : EncryptionParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// The certificate holding the public key used for encryption.
|
|
@ -59,8 +59,10 @@
|
|||
<Compile Include="Communication\Events\MessageBoxRequestEventArgs.cs" />
|
||||
<Compile Include="Configuration\Cryptography\EncryptionParameters.cs" />
|
||||
<Compile Include="Configuration\Cryptography\ICertificateStore.cs" />
|
||||
<Compile Include="Configuration\Cryptography\IPasswordEncryption.cs" />
|
||||
<Compile Include="Configuration\Cryptography\IPublicKeyEncryption.cs" />
|
||||
<Compile Include="Configuration\Cryptography\PasswordParameters.cs" />
|
||||
<Compile Include="Configuration\Cryptography\PublicKeyHashParameters.cs" />
|
||||
<Compile Include="Configuration\Cryptography\PublicKeyParameters.cs" />
|
||||
<Compile Include="Configuration\DataFormats\FormatType.cs" />
|
||||
<Compile Include="Configuration\DataCompression\IDataCompressor.cs" />
|
||||
<Compile Include="Configuration\Cryptography\IHashAlgorithm.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<AssemblyCopyrightAttribute>().Copyright;
|
||||
var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||
var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue