-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fold inbox crypto into one assembly #64307
Conversation
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsThis change does not move any of the tests associated with the code moves. For System.Security.Cryptography.Csp.Tests.dll, For System.Security.Cryptography.X509Certificates.Tests.dll, the tests could be moved, This unification revealed several places were our OS support attributes weren't quite right,
|
@@ -11,432 +11,425 @@ | |||
|
|||
namespace System.Security.Cryptography | |||
{ | |||
#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS | |||
internal static partial class DSAImplementation | |||
public sealed partial class DSACng : DSA |
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.
All of the implementation files that are in libraries/Common/src/... could be moved to under System.Security.Cryptography at this point; but I chose not to move them at this time to keep the PR a bit more... sane.
@@ -115,14 +112,17 @@ internal static CngAlgorithm EcdhCurveNameToAlgorithm(string name) | |||
{ | |||
case "nistP256": | |||
case "ECDH_P256": | |||
case "ECDSA_P256": |
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.
There were two copies of these, one had this line, the other didn't. Apparently one got hit by ECDiffieHellman.Create(), the other git hit by ECDiffieHellmanCng.
@vcsjones / @filipnavara The failing tests all seem to look like using (ECDsa key = ECDsa.Create(...))
{
Make/get a cert from somewhere
cert.CopyWithPrivateKey(key); // <-- fails
} This is because previously the as check in CopyWithPrivateKey failed for ECDsa.Create() (it was an ECSecurityTransforms, but from a different assembly, so the as said no/null). Presumably the failure is that either Having gotten that far in the investigation my brain is sort of fried, so looking for easy pointers on how to update the check ( Lines 490 to 496 in afa4b2f
ECDsa.Create() and ECDiffieHellman.Create() both use the wrapper type that CNG/OpenSsl use... that'd probably hide the problem in the short term... but if there's an easier check, I'd love to know what it is.
Interestingly, RSA doesn't have that problem, and either DSA doesn't or we don't test DSA. |
@bartonjs I can take a look Friday / Saturday. This rings a bell when I spiked out the key services changes. |
Sounds like we're running in to the situation I ran in to in the 3rd paragraph of my spike: #38101 |
It definitely rings a bell. I think I intentionally ignored the scenario because it couldn't happen when the implementations were in different assemblies. Mixing the two types of keys (keychain/CSSM vs. the new iOS-style one) just doesn't work in many cases so recreating the ephemeral keypair by export+import is probably the best that can be done. It should also result in the fast native implementation being used. The Essentially it boils down to couple of options:
The Apple's code is much more forgiving when mixing the legacy CSSM keys and the iOS-style keys with RSA.
DSA has only single legacy implementation (CSSM) in the Apple's native code. There's no DSA in iOS and also no iOS-style DSA keys on macOS. Thus |
Here's a proof of concept fix: ed16db6 |
Well... that path would be the only one that works if you have a non-exportable key stored in a keychain. Since we don't expose the Apple interop types as public API the only way to get one of those is from GetECDsaPrivateKey(). (Which, basically limits the scenario to "I want to renew/regen a cert with the same key", I suppose) Thanks for the workaround code. I don't like duplicating the logic of the "internal representation" of Apple EC keys, but I think I see a path forward from that. |
I had the same line of thinking and went back and forth with striking the option from the list :-) Eventually I left it there to paint the full picture so someone without sleep deprivation can re-read it...
Not a big fan of it myself but I wanted to unblock the PR. I've tried to implement other options from the list but they were quite a big mess. The iOS-style keys only support X.963 format export, the legacy |
87be37c
to
1243105
Compare
Instead of duplicating the data format handler, I factored it out in 1243105. More total lines, but gives me a bit of a warm fuzzy feeling. |
Tests look good |
This change leaves the tests in System.Security.Cryptography.Csp.Tests.dll, because the crypto test infrastructure has not yet been upgraded to allow for two algorithm provider tests in the same assembly (e.g. not both Aes.Create() and AesCryptoServiceProvider can be tested)
1243105
to
ad5142e
Compare
Rebased for merge conflicts... again... hopefully for the last time. |
src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
Show resolved
Hide resolved
@@ -94,7 +91,7 @@ public static HashAlgorithmName NameOrOidToHashAlgorithmName(string nameOrOid) | |||
return null; | |||
} | |||
|
|||
public static HashAlgorithmName? OidToHashAlgorithmName(string oid) | |||
internal static HashAlgorithmName? OidToHashAlgorithmName(string oid) |
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 think this can be replaced entirely by HashAlgorithmName.TryFromOid
now. Don't have to do it here, just leaving a note as something one of us can do afterward.
// Do not wrap ExportPkcs8PrivateKey, let it fall back to reconstructing it from parameters | ||
// so that the ECDiffieHellman.Create()-returned object uses the same set of attributes on all platforms. | ||
// (CNG adds the key usage attribute to distinguish ECDSA from ECDH) | ||
//public override byte[] ExportPkcs8PrivateKey() => _wrapped.ExportPkcs8PrivateKey(); |
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.
Interesting. It this just so we get bit-for-bit (or thereabouts) the same exports on platforms? Seems reasonable, but is there now a discrepancy between this attribute's presence between encrypted and non-encrypted PKCS8 exports?
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.
Hm, you're right, I might need to block the encrypted ones as well.
We had a test for the unencrypted ones. Encrypted export from CNG is special, since they have complicated export permissions... but that shouldn't apply for ECDsa.Create() keys.
public static partial ECDsa Create(ECParameters parameters) | ||
{ | ||
ECDsa ecdsa = Create(); | ||
ecdsa.ImportParameters(parameters); |
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.
Do we want to catch, dispose, re-throw here?
public static partial ECDsa Create(ECParameters parameters) | ||
{ | ||
ECDsa ec = new ECDsaCng(); | ||
ec.ImportParameters(parameters); |
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.
Same catch, dispose, re-throw question.
ECDsa ec = new ECDsaImplementation.ECDsaCng(); | ||
ec.ImportParameters(parameters); | ||
return ec; | ||
_core.Dispose(); |
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.
Should this be inside if (disposing)
? Is finalizer somewhere going to mean _core.Dispose()
going to get disposed twice?
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.
For sanity/consistency, probably. But since the Cng classes are sealed, there's not a sneaky place a finalizer can come in. While it looks like I massively rewrote this file, it's really "move the one from the other project over top this", where the Dispose already looked like that.
|
||
protected override void Dispose(bool disposing) | ||
{ | ||
_core.Dispose(); |
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.
Another if (disposing)
question.
src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
Show resolved
Hide resolved
Aside from the |
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 wouldn't consider any of my remaining feedback blocking. It can be done in follow up PRs, if chosen to be done at all.
|
||
protected abstract bool ReleaseNativeHandle(); | ||
} | ||
public sealed partial class SafeNCryptKeyHandle : SafeNCryptHandle |
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 missing newline before type
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.
and below..
{ | ||
return new ECDsaImplementation.ECDsaCng(); | ||
get |
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.
someone can change these to expression bodied form someday.
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 there a note somewhere explaining the resaoning behind these moves, as I'm out of the loop?
@danmoseley the issue for this is at #55690. |
@ViktorHofer -- seems like this should reduce the number of P2Ps we require in the repo if I'm not mistaken. Could be interesting for the build perf work. @bartonjs -- did you consider removing P2Ps to assemblies which are now full facades from other projects in the repo? |
@ericstj I feel like I tried it in the earlier phases and almost everything had to be put back (might be observation bias error), so I didn't look into it for this phase. But now that it's complete I can believe there's probably some reduction to be had. |
@bartonjs I'm seeing noticeable improvements for "publish size", this PR is the only suspect 👍 https://aka.ms/aspnet/benchmarks |
This change does not move any of the tests associated with the code moves.
For System.Security.Cryptography.Csp.Tests.dll,
System.Security.Cryptography.Cng.Tests.dll, and
System.Security.Cryptography.OpenSsl.Tests.dll, the crypto test infrastructure needs
to be updated to allow for multiple algorithm providers to be tested at once.
Currently, those test libraries have static provider/factory types that say that (e.g.)
the RSA to test is RSA.Create() / RSACryptoServiceProvider / RSACng / RSAOpenSsl.
For System.Security.Cryptography.X509Certificates.Tests.dll, the tests could be moved,
but require a different level of thinking for path and namespace unification than was
required for previous phases. So, for consistency within this PR, the X509 tests also
aren't moving yet.
This unification revealed several places were our OS support attributes weren't quite right,
due to implementations being automatically generated as PNSE.
[SupportedOS("windows")]
[UnsupportedOS("android")] / browser / ios / tvos / windows
(works on macOS and linux)[UnsupportedOS("browser")]
[UnsupportedOS("browser")]
. It's unlikely to be used as an exchange type, so the type got painted instead of constructors.