Add ToXmlString and FromXmlString implementations to RSA and DSA. #37593
Conversation
The ToXmlString implementations produce output identical to .NET Framework. The FromXmlString implementations are based on XDocument in Core, vs a custom parser in Framework. Additionally, the FromXmlString in Core can read values which (per the xmldsig spec) removed any leading zero-value bytes, whereas the Framework version can't. No ToXmlString or FromXmlString is being added for ECDsa or ECDiffieHellman, because these types have always thrown in .NET Framework. The equivalent functionality was provided by an overload on ECDsaCng (and ECDiffieHellmanCng) that took a format-type enum (with only one member defined in it). Since that's not portable, and telemetry has never seen a caller of that method, they are being left as PNSE.
I wonder why you chose to use |
The reader is (seems to be) a lot harder to work with via reflection (where enums have to be imported or copied).
Once there isn't really element nesting or attributes or anything that needs to be escaped, the writer is just a version of StringBuilder that has to be reflected, so it doesn't really provide value. |
src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAXml.cs
Show resolved
Hide resolved
Exception exception = Assert.ThrowsAny<Exception>( | ||
() => rsa.FromXmlString( | ||
@" | ||
<RSAKeyV |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra enters
Exception exception = Assert.ThrowsAny<Exception>( | ||
() => rsa.FromXmlString( | ||
@" | ||
<RSAKeyV |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if this is something we should fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What kind of fix? Adding a namespace might break someone who is expecting it to not have one. The wrong namespace, and Base64Binary vs CryptoBinary (which, if fixed, would break import back in netfx) are just characteristics of these methods as deviations from the spec.
I think the design assumed it was being rendered inside an element that already declared the implicit inherited namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fine with me, although I think if you try to nest it inside such document (using XML APIs) it will explicitly remove that namespace though
{ | ||
Type xDocument = | ||
Type.GetType( | ||
"System.Xml.Linq.XDocument, System.Private.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you depend on System.Xml.XDocument instead of this? (it should forward to System.Private.Xml.Linq)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. System.Private.Xml touches networking, networking touches crypto, so I can't touch System.Private.Xml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that only runtime dependency though? https://github.com/dotnet/corefx/blob/master/src/System.Xml.ReaderWriter/ref/System.Xml.ReaderWriter.csproj touches only System.Net.Primitives
why is the reflection necessary? (I'm fine with using StringBuilder instead of XmlWriter as a workaround since all of the values seem to be known and encoded as base64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM codewise (with few tiny comments). Not sure if reflection is necessary though, if possible I'd prefer if we just use one of the old contracts (System.Xml.ReaderWriter/System.Xml.XDocument) - is there some layering issue there? cc: @ericstj
Buffer.BlockCopy(ret, 0, ret, shift, written); | ||
ret.AsSpan(0, shift).Clear(); | ||
return ret; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not fully understanding the logic here. What's the purpose of sizeHint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSA.ImportParameters, and DSA.ImportParameters, have explicit requirements on the relationships of the sizes of the parameters.
For RSA: P, Q, DP, DQ, InverseQ must have lengths equal to half (round up) of Modulus and D must have the same length as Modulus.
For DSA: G and Y have to match P, X has to match Q.
if (!_enumerator.MoveNext()) | ||
{ | ||
idx = -1; | ||
_enumerator = _enumerable.GetEnumerator(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we loop through the whole enumerable many times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In happy degenerate cases: just once. But every missing optional property requires checking all the values. Basically I modelled it after how it was already working in NetFX; but I had thought (and am now thinking again) that a one-loop-into-a-switch might be better.
Hm, it does look like I can reference System.Xml.ReaderWriter from System.Security.Cryptography.Algorithms... I feel like that used to be a cyclic dependency build error. There's definitely a runtime problem:
I haven't made it as far as exploring if I can actually run the code... but: is using the facades like this stable? What we're really doing here is inducing a cyclic dependency, does the loader handle it OK? Deterministically? I'm torn between pursuing the correctness of using XmlReader for reading Xml and sticking with the runtime reflection (cyclic) dependency, since that's a dependency model I've seen work for years. |
In the interest of getting to other bugs, I'm going to merge this as-is, and I opened #37664 to see if we can avoid reflection. |
The ToXmlString implementations produce output identical to .NET Framework.
The FromXmlString implementations are based on XDocument in Core, vs a
custom parser in Framework. Additionally, the FromXmlString in Core can
read values which (per the xmldsig spec) removed any leading zero-value
bytes, whereas the Framework version can't.
No ToXmlString or FromXmlString is being added for ECDsa or
ECDiffieHellman, because these types have always thrown in .NET Framework.
The equivalent functionality was provided by an overload on ECDsaCng (and
ECDiffieHellmanCng) that took a format-type enum (with only one member
defined in it). Since that's not portable, and telemetry has never
seen a caller of that method, they are being left as PNSE.
Fixes #23686.