Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Add ToXmlString and FromXmlString implementations to RSA and DSA. #37593

Merged
merged 10 commits into from May 15, 2019

Conversation

bartonjs
Copy link
Member

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.

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.
@bartonjs bartonjs added this to the 3.0 milestone May 10, 2019
@bartonjs bartonjs self-assigned this May 10, 2019
@filipnavara
Copy link
Member

I wonder why you chose to use XDocument and custom writers as opposed to XmlReader and XmlWriter which seem to be more suitable.

@bartonjs
Copy link
Member Author

I wonder why you chose to use XDocument ... as opposed to XmlReader

The reader is (seems to be) a lot harder to work with via reflection (where enums have to be imported or copied).

I wonder why you chose to use ... custom writers as opposed to ... XmlWriter

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.

@jingsunsql jingsunsql added this to In progress in .NET Core impacting internal partners via automation May 13, 2019
@jingsunsql jingsunsql added this to In progress in .NET Core impacting internal partners via automation May 13, 2019
Exception exception = Assert.ThrowsAny<Exception>(
() => rsa.FromXmlString(
@"
<RSAKeyV
Copy link
Member

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
Copy link
Member

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

Copy link
Member Author

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.

Copy link
Member

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");
Copy link
Member

@krwq krwq May 14, 2019

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)

Copy link
Member Author

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.

Copy link
Member

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

@krwq
Copy link
Member

krwq commented May 14, 2019

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)

Copy link
Member

@krwq krwq left a 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;
}
Copy link
Member

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?

Copy link
Member Author

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();
Copy link
Member

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?

Copy link
Member Author

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.

@bartonjs
Copy link
Member Author

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:

  • System.Private.Xml references System.Net.Requests
  • System.Net.Requests references System.Security.Cryptography.X509Certificates
  • System.Security.Cryptography.X509Certificates references System.Security.Cryptography.Algorithms

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.

@bartonjs
Copy link
Member Author

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.

@bartonjs bartonjs merged commit 271138b into dotnet:master May 15, 2019
.NET Core impacting internal partners automation moved this from In progress to Done May 15, 2019
@bartonjs bartonjs deleted the asymm_tofrom_xml branch May 15, 2019 05:55
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
4 participants