Skip to content

Commit

Permalink
Merge pull request #1 from tintoy/anthonylangsworth-monotests
Browse files Browse the repository at this point in the history
Implement DsaKeyValue.LoadXml
  • Loading branch information
anthonylangsworth committed Feb 24, 2017
2 parents 0433de5 + 9ab96f4 commit d2e4569
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,51 +44,78 @@ public DSA Key
// public methods
//

/// <summary>
/// Create an XML representation.
/// </summary>
/// <remarks>
/// Based upon https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue.
/// </remarks>
/// <returns>
/// An <see cref="XmlElement"/> containing the XML representation.
/// </returns>
public override XmlElement GetXml()
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
return GetXml(xmlDocument);
}

private const string KeyValueElementName = "KeyValue";
private const string DSAKeyValueElementName = "DSAKeyValue";

//Optional {P,Q}-Sequence
private const string PElementName = "P";
private const string QElementName = "Q";

//Optional Members
private const string GElementName = "G";
private const string JElementName = "J";

//Mandatory Members
private const string YElementName = "Y";

//Optional {Seed,PgenCounter}-Sequence
private const string SeedElementName = "Seed";
private const string PgenCounterElementName = "PgenCounter";

internal override XmlElement GetXml(XmlDocument xmlDocument)
{
DSAParameters dsaParams = _key.ExportParameters(false);

XmlElement keyValueElement = xmlDocument.CreateElement("KeyValue", SignedXml.XmlDsigNamespaceUrl);
XmlElement dsaKeyValueElement = xmlDocument.CreateElement("DSAKeyValue", SignedXml.XmlDsigNamespaceUrl);
XmlElement keyValueElement = xmlDocument.CreateElement(KeyValueElementName, SignedXml.XmlDsigNamespaceUrl);
XmlElement dsaKeyValueElement = xmlDocument.CreateElement(DSAKeyValueElementName, SignedXml.XmlDsigNamespaceUrl);

XmlElement pElement = xmlDocument.CreateElement("P", SignedXml.XmlDsigNamespaceUrl);
XmlElement pElement = xmlDocument.CreateElement(PElementName, SignedXml.XmlDsigNamespaceUrl);
pElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.P)));
dsaKeyValueElement.AppendChild(pElement);

XmlElement qElement = xmlDocument.CreateElement("Q", SignedXml.XmlDsigNamespaceUrl);
XmlElement qElement = xmlDocument.CreateElement(QElementName, SignedXml.XmlDsigNamespaceUrl);
qElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Q)));
dsaKeyValueElement.AppendChild(qElement);

XmlElement gElement = xmlDocument.CreateElement("G", SignedXml.XmlDsigNamespaceUrl);
XmlElement gElement = xmlDocument.CreateElement(GElementName, SignedXml.XmlDsigNamespaceUrl);
gElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.G)));
dsaKeyValueElement.AppendChild(gElement);

XmlElement yElement = xmlDocument.CreateElement("Y", SignedXml.XmlDsigNamespaceUrl);
XmlElement yElement = xmlDocument.CreateElement(YElementName, SignedXml.XmlDsigNamespaceUrl);
yElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Y)));
dsaKeyValueElement.AppendChild(yElement);

// Add optional components if present
if (dsaParams.J != null)
{
XmlElement jElement = xmlDocument.CreateElement("J", SignedXml.XmlDsigNamespaceUrl);
XmlElement jElement = xmlDocument.CreateElement(JElementName, SignedXml.XmlDsigNamespaceUrl);
jElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.J)));
dsaKeyValueElement.AppendChild(jElement);
}

if (dsaParams.Seed != null)
{ // note we assume counter is correct if Seed is present
XmlElement seedElement = xmlDocument.CreateElement("Seed", SignedXml.XmlDsigNamespaceUrl);
XmlElement seedElement = xmlDocument.CreateElement(SeedElementName, SignedXml.XmlDsigNamespaceUrl);
seedElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Seed)));
dsaKeyValueElement.AppendChild(seedElement);

XmlElement counterElement = xmlDocument.CreateElement("PgenCounter", SignedXml.XmlDsigNamespaceUrl);
XmlElement counterElement = xmlDocument.CreateElement(PgenCounterElementName, SignedXml.XmlDsigNamespaceUrl);
counterElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(Utils.ConvertIntToByteArray(dsaParams.Counter))));
dsaKeyValueElement.AppendChild(counterElement);
}
Expand All @@ -98,10 +125,82 @@ internal override XmlElement GetXml(XmlDocument xmlDocument)
return keyValueElement;
}

/// <summary>
/// Deserialize from the XML representation.
/// </summary>
/// <remarks>
/// Based upon https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue.
/// </remarks>
/// <param name="value">
/// An <see cref="XmlElement"/> containing the XML representation. This cannot be null.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="value"/> cannot be null.
/// </exception>
/// <exception cref="CryptographicException">
/// The XML has the incorrect schema or the DSA parameters are invalid.
/// </exception>
public override void LoadXml(XmlElement value)
{
// Until DSA implements FromXmlString, throw here
throw new PlatformNotSupportedException();
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (value.Name != KeyValueElementName
|| value.NamespaceURI != SignedXml.XmlDsigNamespaceUrl)
{
throw new CryptographicException($"Root element must be {KeyValueElementName} element in namepsace {SignedXml.XmlDsigNamespaceUrl}");
}

const string xmlDsigNamespacePrefix = "dsig";
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(value.OwnerDocument.NameTable);
xmlNamespaceManager.AddNamespace(xmlDsigNamespacePrefix, SignedXml.XmlDsigNamespaceUrl);

XmlNode dsaKeyValueElement = value.SelectSingleNode($"{xmlDsigNamespacePrefix}:{DSAKeyValueElementName}", xmlNamespaceManager);
if (dsaKeyValueElement == null)
{
throw new CryptographicException($"{KeyValueElementName} must contain child element {DSAKeyValueElementName}");
}

XmlNode yNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{YElementName}");
if (yNode == null)
throw new CryptographicException($"{YElementName} is missing");

XmlNode pNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{PElementName}");
XmlNode qNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{QElementName}");

if ((pNode == null && qNode != null) || (pNode != null && qNode == null))
throw new CryptographicException($"{PElementName} and {QElementName} can only occour in combination");


XmlNode gNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{GElementName}");
XmlNode jNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{JElementName}");

XmlNode seedNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{SeedElementName}");
XmlNode pgenCounterNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{PgenCounterElementName}");
if ((seedNode == null && pgenCounterNode != null) || (seedNode != null && pgenCounterNode == null))
throw new CryptographicException($"{SeedElementName} and {PgenCounterElementName} can only occur in combination");

try
{
Key.ImportParameters(new DSAParameters
{
P = (pNode != null) ? Convert.FromBase64String(pNode.InnerText) : null,
Q = (pNode != null) ? Convert.FromBase64String(qNode.InnerText) : null,
G = (gNode != null) ? Convert.FromBase64String(gNode.InnerText) : null,
Y = Convert.FromBase64String(yNode.InnerText),
J = (jNode != null) ? Convert.FromBase64String(jNode.InnerText) : null,
Seed = (seedNode != null) ? Convert.FromBase64String(seedNode.InnerText) : null,

//https://github.com/peterwurzinger
//TODO: I don't know if zero (0) is the correct default value if the counter-element is missing
Counter = (seedNode != null) ? BitConverter.ToInt32(Convert.FromBase64String(pgenCounterNode.InnerText), 0) : 0
});
}
catch (Exception ex)
{
throw new CryptographicException($"An error occurred parsing the key components", ex);
}
}
}
}
23 changes: 17 additions & 6 deletions src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ public void Ctor_Dsa()
public void Ctor_Dsa_Null()
{
DSAKeyValue dsaKeyValue = new DSAKeyValue(null);
Assert.NotNull(dsaKeyValue.Key);

//From https://github.com/peterwurzinger:
//This assertion is incorrect, since the parameter value is stored unvalidated/unprocessed
//Assert.NotNull(dsaKeyValue.Key);

Assert.Null(dsaKeyValue.Key);
}

[Fact]
Expand All @@ -49,9 +54,12 @@ public void GetXml()
DSAKeyValue dsa = new DSAKeyValue();
XmlElement xmlkey = dsa.GetXml();

//From https://github.com/peterwurzinger:
//According to the schema (http://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue), the only parameter to occur necessarily is 'Y'. Rework?

// Schema check. Should not throw.
const string schema = "http://www.w3.org/2000/09/xmldsig#";
new [] { "P", "Q", "G", "Y", "J", "Seed", "PgenCounter"}
new [] { "P", "Q", "G", "Y", "Seed", "PgenCounter"}
.Select(elementName => Convert.FromBase64String(xmlkey.SelectSingleNode($"*[local-name()=DSAKeyValue and namespace-uri()='{schema}']/*[local-name()='{elementName}' and namespace-uri()='{schema}']").InnerText))
.ToArray();
}
Expand All @@ -70,22 +78,25 @@ public void GetXml_SameDsa()
[Fact]
public void LoadXml_PlatformNotSupported()
{
//https://github.com/peterwurzinger
//I kind of 'fixed' this test. The value above, which is now commented, is the string which was compared against the XML of an empty DsaKeyValue - instance, which differs completely. Has something been lost in a commit?

//string dsaKey = "<KeyValue xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><DSAKeyValue><P>xc+QZRWTgr390gzwNXF+WzoepZkvAQvCzfCm+YyXj0KPoeHHeSc5ORzXQw81V+7XJR3gupvlI4F7lW9YC538l+3eqGm8IQlCIS+U+7ICTDOFFKevqsYX0BnjO0vvE4aAtDyxfSOTCOAo1cJ+6G6xgcC1JGIBEYCtg1tH8wUewDE=</P><Q>yyfZb0S/rimXl9ScJ3zIba2oGl8=</Q><G>crLazMg+vgI7u6+Idgi9iTLdRa4fptat3gdY97zcc857+OVdmT+lVRpK3okWpmBbw2wSffU8QltwFf42BVs+/HGUOUo2hNqSSXgzl1i+1frO7/cqooHVcy5WX0xxaIPsKcREPI5pNPj/3g8apTgErLMGsHkFdngwbMed9DArTks=</G><Y>FlAozo17wV/LCMRrtnmMKxVQNpidJVkZNM1/0eR65x8giwPs6yXzJmFT8f2tmPJY2FIOAtp5JYin4xUhwIHF452Gg50wUrjV6WTGkiC+gzLC2fVIyGlVsFecLj6ue7J+MACG+b3NQnxFuT5maQnPnEeuGgjLXfwYsAR1vfU0Gas=</Y><J>+UPMvUPq9Fo6Q1fr2oEYDxfGMMtfdoQmVBxI+TkUYQsReodRzBbnvGV1uPLWTpKKd/uJNUHO/QGb05Cvc6u49/AToDJIyi4e01hTLNCzeQk/Hj19gowb5wkTIjyaH04VyPE5zYoTYfuu3Y3Q</J><Seed>+cvoO7bzdpAwAjnDDApPzBCl6zg=</Seed><PgenCounter>ATM=</PgenCounter></DSAKeyValue></KeyValue>";
string dsaKey = "<KeyValue xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><DSAKeyValue><P>xc+QZRWTgr390gzwNXF+WzoepZkvAQvCzfCm+YyXj0KPoeHHeSc5ORzXQw81V+7XJR3gupvlI4F7lW9YC538l+3eqGm8IQlCIS+U+7ICTDOFFKevqsYX0BnjO0vvE4aAtDyxfSOTCOAo1cJ+6G6xgcC1JGIBEYCtg1tH8wUewDE=</P><Q>yyfZb0S/rimXl9ScJ3zIba2oGl8=</Q><G>crLazMg+vgI7u6+Idgi9iTLdRa4fptat3gdY97zcc857+OVdmT+lVRpK3okWpmBbw2wSffU8QltwFf42BVs+/HGUOUo2hNqSSXgzl1i+1frO7/cqooHVcy5WX0xxaIPsKcREPI5pNPj/3g8apTgErLMGsHkFdngwbMed9DArTks=</G><Y>FlAozo17wV/LCMRrtnmMKxVQNpidJVkZNM1/0eR65x8giwPs6yXzJmFT8f2tmPJY2FIOAtp5JYin4xUhwIHF452Gg50wUrjV6WTGkiC+gzLC2fVIyGlVsFecLj6ue7J+MACG+b3NQnxFuT5maQnPnEeuGgjLXfwYsAR1vfU0Gas=</Y><J>+UPMvUPq9Fo6Q1fr2oEYDxfGMMtfdoQmVBxI+TkUYQsReodRzBbnvGV1uPLWTpKKd/uJNUHO/QGb05Cvc6u49/AToDJIyi4e01hTLNCzeQk/Hj19gowb5wkTIjyaH04VyPE5zYoTYfuu3Y3Q</J><Seed>+cvoO7bzdpAwAjnDDApPzBCl6zg=</Seed><PgenCounter>ATM=</PgenCounter></DSAKeyValue></KeyValue>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(dsaKey);

DSAKeyValue dsa1 = new DSAKeyValue();
Assert.Throws<PlatformNotSupportedException>(() => dsa1.LoadXml(doc.DocumentElement));

//string s = (dsa1.GetXml().OuterXml);
//Assert.Equal(dsaKey, s);
string s = (dsa1.GetXml().OuterXml);
Assert.Equal(dsaKey, s);
}

[Fact]
public void LoadXml_Null()
{
DSAKeyValue dsa1 = new DSAKeyValue();
Assert.Throws<PlatformNotSupportedException>(() => dsa1.LoadXml(null));
Assert.Throws<ArgumentNullException>(() => dsa1.LoadXml(null));
}
}
}

0 comments on commit d2e4569

Please sign in to comment.