runtime: Annotate unsupported APIs in System.Security.*

As part of https://github.com/dotnet/runtime/issues/47228 i am running an analyzer to detect APIs throwing PNSE but not annotated with Obsolete/SupportedOSPlatform/UnsupportedOSPlatform attributes, we need to annotate them so that developers get warnings when they use them unexpectedly

For now, I have results only for cross-platform builds, analysis of targeted builds are coming soon where more APIs could be detected

API Comment Suggestion Location
‘SecurityContext.Capture()’ throws unconditionally All APIs throws [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(11,51)
‘SecurityContext.CreateCopy()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(12,47)
‘SecurityContext.Dispose()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(13,33)
‘SecurityContext.IsFlowSuppressed()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(14,49)
‘SecurityContext.IsWindowsIdentityFlowSuppressed()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(15,64)
‘SecurityContext.RestoreFlow()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(16,44)
‘SecurityContext.Run(SecurityContext, ContextCallback, object)’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(17,107)
‘SecurityContext.SuppressFlow()’ throws unconditionally [Obsolete] the type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(18,57)
‘SecurityContext.SuppressFlowWindowsIdentity()’ throws unconditionally [Obsolete] entire type libraries\System.Security.Permissions\src\System\Security\SecurityContext.cs(19,72)

EDIT: Adding APIs found for netstandard targeted builds

API Comment Suggestion Location
‘ECDiffieHellmanCng.FromXmlString(string, ECKeyXmlFormat)’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDiffieHellmanCng.Xml.cs(10,13)
‘ECDiffieHellmanCng.ToXmlString(ECKeyXmlFormat)’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDiffieHellmanCng.Xml.cs(15,13)
‘ECDiffieHellmanCngPublicKey.ToXmlString()’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDiffieHellmanCngPublicKey.cs(37,13)
‘ECDiffieHellmanCngPublicKey.FromXmlString(string)’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDiffieHellmanCngPublicKey.cs(42,13)
‘ECDsaCng.FromXmlString(string, ECKeyXmlFormat)’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDsaCng.cs(131,16)
‘ECDsaCng.ToXmlString(ECKeyXmlFormat)’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Cng\src\System\Security\Cryptography\ECDsaCng.cs(143,16)
‘ECDiffieHellmanOpenSslPublicKey.ToXmlString()’ Internal API, unconditionally throwing obsolet if referenced in public API libraries\Common\src\System\Security\Cryptography\ECDiffieHellmanOpenSslPublicKey.cs(42,17)
‘ECDiffieHellmanOpenSslPublicKey.ToByteArray()’ Internal API, unconditionally throwing obsolete if referenced in public API libraries\Common\src\System\Security\Cryptography\ECDiffieHellmanOpenSslPublicKey.cs(47,17)
‘CmsSigner.CmsSigner(CspParameters)’ It has comment possibly adding windows support but no issue attached, obsolete libraries\System.Security.Cryptography.Pkcs\src\System\Security\Cryptography\Pkcs\CmsSigner.cs(63,55)
‘SignerInfo.ComputeCounterSignature()’ Uncondidionally throws obsolete libraries\System.Security.Cryptography.Pkcs\src\System\Security\Cryptography\Pkcs\SignerInfo.cs(296,13)
‘PermissionSet.PermitOnly()’ Uncondidionally throws it has ifdef-ed obsolete libraries\System.Private.CoreLib\src\System\Security\PermissionSet.cs(43,36)
‘CodeAccessPermission.PermitOnly()’ Uncondidionally throws it has ifdef-ed obsolete libraries\System.Security.Permissions\src\System\Security\CodeAccessPermission.cs(22,36)

Note: We are suggesting to add [Obsolete] for the APIs only supported in .Net framework but not supported in .NetCore, with the corresponding Message, DiagnosticId, and UrlFormat https://github.com/dotnet/runtime/blob/cecc76a29a8b6c1fca181a38f076b4d949081746/src/libraries/System.Net.Requests/src/System/Net/AuthenticationManager.cs#L17 You can claim the next available DiagnosticId from Obsoletions.cs .

If it is only supported on a specific platform(s) consider adding [SupportedOSPlatform(“platformName”)] or if it is unsupported on a specific platform(s) [UnsupportedOSPlatform(“platformName”)] attribute instead

cc @jeffhandley @terrajobst @GrabYourPitchforks

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 38 (36 by maintainers)

Most upvoted comments

The constructor is… harder?

Yeah. But then it feels weird to have a constructor that takes the key blob and no not-obsolete way to do anything with it.

Since it’s a protected constructor on a class that no one is deriving from I would wager we could take a swing at obsoleting it with less than perfect wording.

OK, the landscape here is complicated, but in general it looks like @bartonjs’s wager holds up, particularly through the lens of “ecosystem code exposed to such a change”. I definitely found types that inherit from System.Security.Cryptography.ECDiffieHellmanPublicKey, but almost universally these are types in our own shipped assemblies (System.Core, System.Security.Cng/Algorithms/OpenSsl). These assemblies are in our own packages, as well as redistributed in other packages at a pretty high rate. None of those really represent problematic scenarios WRT deprecation. The few that were genuinely non-product types are in assemblies that are not published on NuGet.org, and instead come from other locations like internal test trees/tools.

I need to figure out how to publish a few NuGet packages, then.

Had there been an actual “eat my hat” type consequence I definitely would have thrown in a disclaimer like “(offer valid only for non-prerelease packages already present on nuget.org at the original time of this post)”. I may not be smart enough to remember to look in partial files, but I’m not that naive 😄.

I’ll tackle the rest of these over the next day or two.

  • Rfc2898DeriveBytes.CryptDeriveKey obsolete
    • Agreed
  • System.Security.SecurityContext obsolete
    • Agreed (merged!)
  • CmsSigner.CmsSigner(CspParameters) obsolete
    • Agreed (double checked that we didn’t make it work on Windows)
  • SignerInfo.ComputeCounterSignature() obsolete
    • Agreed (double checked that we didn’t make it work on Windows)
  • RC2.Create() see below

For RC2-browser we seem to have marked the whole type, which was probably a bit of an overreach, because someone /could/ provide a working implementation of RC2. RC2.Create() on android I’m pretty sure succeeds, but returns an object that fails if you try to actually use it. Part of me thinks it’d be better to just put the PNSE there, but maybe there’s a legit use case I can think of for the dummy RC2 instance.

So… yeah, marking RC2.Create as Unsupported-android is probably right. With a comment that says a more formal version of “yeah, it returns, but the object is going to throw if you call CreateEncryptor or CreateDecryptor… so it’s the same as not working”

As for other things in @buyaa-n 's tables:

  • ECDiffieHellmanCng.ToXmlString/FromXmlString
    • Obsolete.
    • We never added them in Core, no one ever complained (to my recollection). The base class version literally never worked (even in NetFX when it was added), and the derived type is Windows-only.
  • ECDiffieHellmanCngPublicKey.ToXmlString/FromXmlString
    • Obsolete (same as above, probably same diagnostic ID)
  • ECDsaCng.ToXmlString/FromXmlString
    • Obsolete (same as above, probably same diagnostic ID)
  • ECDiffieHellmanPublicKey.ToXmlString
    • Obsolete
    • We’ve never had a derived type in Core that didn’t throw. Let’s just obsolete the base type’s version. Maybe the same diagnostic ID as above.
    • Wasn’t directly on the table, but addresses the ECDiffieHellmanOpenSslPublicKey case 😄

I think that only leaves ECDiffieHellmanPublicKey.ToByteArray and ECDiffieHellmanPublicKey…ctor(byte[]). I’d support Obsoleting these. I… thought… we had added SPKI export on this type, but I don’t see it. So maybe we need to hold this one back one release. Using inbox types the ToByteArray only works on Windows, but since it’s a virtual I don’t like putting a SupportedOSAttribute on it.

Adding last findings

Just to summarize findings that I think are actionable:

It would still be useful to have someone else validate this.

We could take these 3 obsoletions without going through API Review–just code review from @bartonjs and @GrabYourPitchforks. I’m comfortable taking them before the August 17th cutoff for .NET 6, especially since it’s not a breaking change.

‘ECDsa.ECDsaImplementation.ECDsaSecurity Transforms.ExportExplicitParameters(bool)’

I’m not sure adding an UnsupportedOSPlatform here would be ergonomic. The issue is that the public API ExportExplicitParameters is accessible via the virtual ECDsa.ExportExplicitParameters. We would have to decorate the virtual with the UOSP attribute, which would not interact well with derived classes (our own or developers’ own). To illustrate:

using System.Runtime.Versioning;

Foo f = new Bar();
f.Do(); // This is actually a `Bar` but since it's called through the base type, a compiler warning appears.

public class Foo
{
    [UnsupportedOSPlatform("iOS")]
    public virtual object Do() => throw new PlatformNotSupportedException();
}

public class Bar : Foo
{
    public override object Do() => new();
}

Similar concerns lie with ToXmlString, FromXmlString, and ToByteArray. It seems a little odd to obsolete an override when a user derived type could override one of these from the base class and implement it.

Some of these do make sense to me to obsolete:

  1. Rfc2898DeriveBytes.CryptDeriveKey - it’s unimplemented and can’t be implemented xplat since it’s a Windows thing. I’m unaware of demand to bring it back.
  2. CmsSigner.CmsSigner(CspParameters) seems weird. It manufactures a certificate on the fly based on those CspParameters. It makes sense to obsolete since it can only be implemented on Windows. If we really, really wanted “make me a cert on the fly” behavior, it probably makes sense to implement a new API that accepted an AsymmetricAlgorithm or something.
  3. SignerInfo.ComputeCounterSignature() There’s an overload that accepts the CmsSigner, so I think it makes sense to obsolete.

I don’t see any immediately actionable things for 6.0. Presumably the obsoletions need to go through API review, and I don’t know if we’re taking any more obsoletions for 6.0 (granted all of them throw, so it’s not exactly a breaking change).

DSA.CreateCore()

The public entry, Create is already marked with UnsupportedOSPlatform:

https://github.com/dotnet/runtime/blob/a2f2a629328a136c2cd18f2cd729e3854de2aeb4/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/DSA.cs#L38

So I think that one is already taken care of.

Some of these APIs that “unconditionally throw” are virtual and are expected to be overridden in derived classes, so while the base class throws, they aren’t necessarily obsolete because derived classes override them and some functionality depend on these classes. I think this would be the case for all of the methods on the HMAC class. So I do not think those should be obsolete or otherwise annotated.