Skip to content
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

OpenSSL provider support #89167

Closed
krwq opened this issue Jul 19, 2023 · 5 comments · Fixed by #104961
Closed

OpenSSL provider support #89167

krwq opened this issue Jul 19, 2023 · 5 comments · Fixed by #104961
Labels
api-approved API was approved in API review, it can be implemented area-System.Security in-pr There is an active PR which will close this issue when it is merged
Milestone

Comments

@krwq
Copy link
Member

krwq commented Jul 19, 2023

In #88656 we've added ENGINE support but we've left out providers pending more investigation on issues discovered during implementation. This API was already approved last year but asking for re-approval given time which passed.
Original issue: #55356

Background

ENGINE and providers are plug-in systems for OpenSSL which allow to make custom implementation of some crypto algorithms. That enables scenarios such as using TPM keys.

ENGINE is an old (now deprecated) plug-in system which was the only FIPS approved plugin system until recently. Providers is new plug-in system allowing a bit more flexibility to developers (but also harder to implement).

Proposal

namespace System.Security.Cryptography
{
    public partial class SafeEvpPKeyHandle
    {
        // existing OpenSSL ENGINE APIs (only plugin model for OSSL 1.1, deprectated (but present) in 3.0+)
        // public static SafeEvpPKeyHandle OpenPrivateKeyFromEngine(string engineName, string keyId) => throw null;
        // public static SafeEvpPKeyHandle OpenPublicKeyFromEngine(string engineName, string keyId) => throw null;

        // OpenSSL Providers (new plugin model for OSSL 3.0+)
        public static SafeEvpPKeyHandle OpenKeyFromProvider(string providerName, string keyUri) => throw null;
    }
}

Usage Example

byte[] data = ...;

// For TPM settings refer to provider documentation you're using, i.e. https://github.com/tpm2-software/tpm2-openssl/tree/master
// specific values are just example
using (SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("tpm2", "handle:0x81000007"))
using (ECDsa ecdsaPri = new ECDsaOpenSsl(priKeyHandle))
{
    byte[] signature = ecdsaPri.SignData(data, HashAlgorithmName.SHA256);
    // do stuff with signature
    // note: tpm2 does not allow Verify operations, public key needs to be exported and re-imported into new instance
}

Security considerations

Those APIs are not meant to be used with untrusted inputs. Allowing untrusted inputs may potentially allow attacker to use TPM or other private keys in an unintended way. Some providers i.e. "default" allow reading files from arbitrary paths (in order to read the key) which makes it further dangerous to use with untrusted inputs. Additionally providerName may load libraries into the process which adds additional risk (depending on OSSL_PROVIDER_set_default_search_path setting this may or may not be a big issue).

(considerations also apply to already added ENGINE APIs)

@krwq krwq added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Security labels Jul 19, 2023
@krwq krwq added this to the 9.0.0 milestone Jul 19, 2023
@ghost
Copy link

ghost commented Jul 19, 2023

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

In #88656 we've added ENGINE support but ENGINEs are deprecated by OpenSSL. #55356 has approved APIs for provider but during testing (#55356 (comment)) we've found that APIs for provider will need to be adjusted since they require OSSL_LIB_CTX* to work correctly with i.e. tpm2 provider. To fix that we could create a container class which would manage lifetime of that.

Example design could look like:

public class OpenSslProvider : IDisposable
{
   public OpenSslProvider(string providerName);

   public RSA OpenRSAPrivateKey(string keyUri); // this could return alternative implementation of RSA which includes all implementation details - downside of that it will need some plumbing in X509Certificate2.CopyWithPrivateKey/X509Certificate2/SslStream etc
   // .. similar for other key types

  // other ideas to consider:
  public SafeEvpPKeyHandle OpenRSAPrivateKey(string keyUri); // this one has downside of having to attach library handle to SafeEvpPKeyHandle handle but all pieces already understand it
  public AssymetricAlgorithm OpenPrivateKey(string keyUri); // similar to above but it would be checking key type and creating appropriate instance
}

This needs a bit more experimentation. I've already done some experimentation trying to get originally designed APIs to work without changes but:

  • tpm2 provider requires that it's loaded in new library context - for me NULL did not work ever - it did load it but none of the keys could be used for signing and they ended up producing errors - this code be bug specific to version of OSSL I was using - I did not try with other versions
  • rather than EVP_PKEY_CTX_new overload taking library context must be used, i.e. EVP_PKEY_CTX_new_from_pkey - this is a change we would need to plumb everywhere

My experiments can be found here: https://github.com/krwq/runtime/blob/open_named_keys/src/libraries/System.Security.Cryptography/tests/osslplugins/testprovider.c

Author: krwq
Assignees: -
Labels:

api-suggestion, area-System.Security

Milestone: 9.0.0

@vcsjones
Copy link
Member

I'm not sure that a new API shape is needed; or that the suggestion here is the right approach. For all other crypto primitives in .NET, the object itself maintains all related handles to keep things working so that two things with two independent lifetimes need to be managed.

Using the example above:

OpenSslProvider provider = new("some_pkcs11_module");
SafeEvpPKeyHandle foo = provider.OpenRSAPrivateKey("my key uri");
provider.Dispose();
RSA rsa = new RSAOpenSsl(foo); // What happens here?

Trying to make that work sensibly, even just making sure we throw a managed exception instead of a SIGSEGV, I think would be difficult.

I think it probably makes sense to have the provider lifetime managed by the SafeEvpPKeyHandle. We have similar concepts already

  1. NCrypt has parent handles:

    protected SafeNCryptHandle(IntPtr handle, SafeHandle parentHandle)

    We use this to keep a CERT_CONTEXT alive when getting a key off of the certificate.

  2. macOS does not require you to manage a keychain lifetime separately from the key itself.

I view the way we could solve this with OSSL_LIB_CTX* similarly: A SafeEvpPKeyHandle could keep a related handle / parent handle so that developers do not have independent object lifetimes to manage.

@krwq krwq added api-ready-for-review API is ready for review, it is NOT ready for implementation blocking Marks issues that we want to fast track in order to unblock other important work and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Jul 11, 2024
@krwq
Copy link
Member Author

krwq commented Jul 11, 2024

Note: I've updated proposal to look as it was before.

@krwq krwq removed blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jul 11, 2024
@krwq
Copy link
Member Author

krwq commented Jul 11, 2024

per offline discussion with @bartonjs it's already approved in #55356 so it doesn't need to be re-approved. PR should point to the old issue which approved it

@terrajobst terrajobst added the api-approved API was approved in API review, it can be implemented label Jul 11, 2024
@terrajobst
Copy link
Contributor

(marking as api-approved for clarity)

@dotnet-policy-service dotnet-policy-service bot added the in-pr There is an active PR which will close this issue when it is merged label Jul 16, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Sep 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Security in-pr There is an active PR which will close this issue when it is merged
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants