credential deserializer now supports variable decryption parameters

This commit is contained in:
David Sparer
2017-01-19 08:47:27 -07:00
parent 4eb1125b7b
commit 4d94c7ac7a
2 changed files with 50 additions and 15 deletions

View File

@@ -18,14 +18,14 @@ namespace mRemoteNGTests.Config.Serializers
public void Setup()
{
_cryptographyProvider = new CryptographyProviderFactory().CreateAeadCryptographyProvider(BlockCipherEngines.AES, BlockCipherModes.GCM);
_deserializer = new XmlCredentialDeserializer(_cryptographyProvider);
_deserializer = new XmlCredentialDeserializer();
}
[Test]
public void HasCorrectId()
{
var id = Guid.NewGuid();
var xml = $"<Credentials>\r\n <Credential Id=\"{id}\" Name=\"testcred\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var xml = $"<Credentials EncryptionEngine=\"AES\" BlockCipherMode=\"GCM\" KdfIterations=\"1000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"{id}\" Name=\"testcred\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.First().Id, Is.EqualTo(id));
}
@@ -34,7 +34,7 @@ namespace mRemoteNGTests.Config.Serializers
public void HasCorrectTitle()
{
const string title = "testtitle";
var xml = $"<Credentials>\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"{title}\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var xml = $"<Credentials EncryptionEngine=\"AES\" BlockCipherMode=\"GCM\" KdfIterations=\"1000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"{title}\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.First().Name, Is.EqualTo(title));
}
@@ -43,7 +43,7 @@ namespace mRemoteNGTests.Config.Serializers
public void HasCorrectUsername()
{
const string username = "myuser";
var xml = $"<Credentials>\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"{username}\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var xml = $"<Credentials EncryptionEngine=\"AES\" BlockCipherMode=\"GCM\" KdfIterations=\"1000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"{username}\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.First().Username, Is.EqualTo(username));
}
@@ -52,7 +52,7 @@ namespace mRemoteNGTests.Config.Serializers
public void HasCorrectPassword()
{
const string plaintextPassword = "mypassword";
var xml = $"<Credentials>\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"myuser\" Password=\"{GeneratePass(plaintextPassword)}\" />\r\n</Credentials>";
var xml = $"<Credentials EncryptionEngine=\"AES\" BlockCipherMode=\"GCM\" KdfIterations=\"1000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"myuser\" Password=\"{GeneratePass(plaintextPassword)}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.First().Password.ConvertToUnsecureString(), Is.EqualTo(plaintextPassword));
}
@@ -60,11 +60,24 @@ namespace mRemoteNGTests.Config.Serializers
[Test]
public void DeserializesAllCredentials()
{
var xml = $"<Credentials>\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n <Credential Id=\"356f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"othertitle\" Username=\"someuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var xml = $"<Credentials EncryptionEngine=\"AES\" BlockCipherMode=\"GCM\" KdfIterations=\"1000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"256f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"testtitle\" Username=\"myuser\" Password=\"{GeneratePass("abc")}\" />\r\n <Credential Id=\"356f4c8c-5819-4226-ad55-6f1f341b5449\" Name=\"othertitle\" Username=\"someuser\" Password=\"{GeneratePass("abc")}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.Count(), Is.EqualTo(2));
}
[Test]
public void CanDecryptNonStandardEncryptions()
{
var otherCryptoProvider = new CryptographyProviderFactory().CreateAeadCryptographyProvider(BlockCipherEngines.Serpent, BlockCipherModes.CCM);
otherCryptoProvider.KeyDerivationIterations = 2000;
const string plaintextPassword = "mypassword";
var encryptedPassword = otherCryptoProvider.Encrypt(plaintextPassword, _key);
var xml =
$"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Credentials EncryptionEngine=\"Serpent\" BlockCipherMode=\"CCM\" KdfIterations=\"2000\" SchemaVersion=\"1.0\">\r\n <Credential Id=\"faadd345-6c68-4891-9897-d22525ec7c58\" Name=\"testcred\" Username=\"davids\" Password=\"{encryptedPassword}\" />\r\n</Credentials>";
var creds = _deserializer.Deserialize(xml, _key);
Assert.That(creds.First().Password.ConvertToUnsecureString(), Is.EqualTo(plaintextPassword));
}
private string GeneratePass(string plaintext)
{
return _cryptographyProvider.Encrypt(plaintext, _key);

View File

@@ -11,27 +11,49 @@ namespace mRemoteNG.Config.Serializers
{
public class XmlCredentialDeserializer
{
private readonly ICryptographyProvider _cryptographyProvider;
public XmlCredentialDeserializer(ICryptographyProvider cryptographyProvider)
{
if (cryptographyProvider == null)
throw new ArgumentNullException(nameof(cryptographyProvider));
_cryptographyProvider = cryptographyProvider;
}
public string SchemaVersion { get; } = "1.0";
public IEnumerable<ICredentialRecord> Deserialize(string xml, SecureString decryptionKey)
{
var xdoc = XDocument.Parse(xml);
ValidateSchemaVersion(xdoc);
var cryptographyProvider = BuildCryptoProvider(xdoc.Root);
var credentials = from element in xdoc.Descendants("Credential")
select new CredentialRecord(Guid.Parse(element.Attribute("Id")?.Value))
{
Name = element.Attribute("Name")?.Value,
Username = element.Attribute("Username")?.Value,
Password = _cryptographyProvider.Decrypt(element.Attribute("Password")?.Value, decryptionKey).ConvertToSecureString(),
Password = cryptographyProvider.Decrypt(element.Attribute("Password")?.Value, decryptionKey).ConvertToSecureString(),
Domain = element.Attribute("Domain")?.Value
};
return credentials;
}
private void ValidateSchemaVersion(XDocument xdoc)
{
var docSchemaVersion = xdoc.Root?.Attribute("SchemaVersion")?.Value;
if (docSchemaVersion != SchemaVersion)
throw new Exception($"The schema version of this document is not supported by this class. Document Version: {docSchemaVersion} Supported Version: {SchemaVersion}");
}
private ICryptographyProvider BuildCryptoProvider(XElement rootElement)
{
if (rootElement == null)
throw new ArgumentNullException(nameof(rootElement));
BlockCipherEngines engine;
Enum.TryParse(rootElement.Attribute("EncryptionEngine")?.Value, true, out engine);
BlockCipherModes mode;
Enum.TryParse(rootElement.Attribute("BlockCipherMode")?.Value, true, out mode);
int kdfIterations;
int.TryParse(rootElement.Attribute("KdfIterations")?.Value, out kdfIterations);
var cryptoProvider = new CryptographyProviderFactory().CreateAeadCryptographyProvider(engine, mode);
cryptoProvider.KeyDerivationIterations = kdfIterations;
return cryptoProvider;
}
}
}