Skip to content

[API Proposal]: Add ECDiffieHellman.DeriveSecretAgreement method #71613

Closed
@renestein

Description

@renestein

Background and motivation

When the need to use the shared secret (agreement) arises, we use our wrapper of the CNG API on Windows (10). We use reflection on non-Windows platforms because the method DeriveSecretAgreement in EcDiffieHellmanOpenSsl class is declared private.

private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash? hasher)

private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash? hasher)

As always, the code that uses reflection depends on the private implementation of the class, which we do not control, so the code is fragile and error-prone. The code without obvious optimizations might look like the following code. We would rather prefer to use public and supported API than this "hack".

 private static CoreECDHAsymmetricAlgorithm TryCreateFrom(object asymmetric, bool failOnError, bool ownsAlgorithm)
        {
#if STD21
            var inner = asymmetric as ECDiffieHellman;
            if (inner == null)
#else
            var inner = asymmetric as AsymmetricAlgorithm;
            if (inner == null || !_ecdhType.IsInstanceOfType(inner))
#endif
            {
                if (failOnError) throw new CryptographicException("ECDiffieHellman algorithm expected.");
                return null;
            }

            if (Environment.Version.Major >= 7)
            {
                FieldInfo wrappedField = inner.GetType().GetField("_wrapped", BindingFlags.Instance | BindingFlags.NonPublic);
                if (wrappedField != null)
                {
#if STD21
                    var wrapped = wrappedField.GetValue(inner) as ECDiffieHellman;
                    if (wrapped != null)
#else
                    var wrapped = wrappedField.GetValue(inner) as AsymmetricAlgorithm;
                    if (_ecdhType.IsInstanceOfType(wrapped))
#endif
                    {
                        inner = wrapped;
                    }
                }
            }

#if STD21
            MethodInfo deriveSecretAgreementMethod = inner.GetType().GetMethod("DeriveSecretAgreement", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(ECDiffieHellmanPublicKey), typeof(IncrementalHash) }, null);
            if (deriveSecretAgreementMethod != null && deriveSecretAgreementMethod.ReturnType != typeof(byte[]))
            {
                deriveSecretAgreementMethod = null;
            }
#else
            MethodInfo deriveSecretAgreementMethod = inner.GetType().GetMethod("DeriveSecretAgreement", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { _ecdhPublicKeyType, typeof(IncrementalHash) }, null);
            if (deriveSecretAgreementMethod == null || deriveSecretAgreementMethod.ReturnType != typeof(byte[]))
            {
                if (failOnError) throw new CryptographicException("Unsupported instance of ECDiffieHellman algorithm.");
                return null;
            }
#endif

//CoreECDHAsymmetricAlgorithm is our class that derives shared secret.
            return new CoreECDHAsymmetricAlgorithm(inner, deriveSecretAgreementMethod, ownsAlgorithm);
        }
        
        
        

API Proposal

namespace System.Security.Cryptography
{
    public partial class ECDiffieHellman : ECAlgorithm
    {
        public virtual byte[] DeriveRawSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey);
    }
}

API Usage

//Create ECDH instance for Bob
using var bobEcdh = ECDiffieHellman.Create();
//Create ECDH instance for Alice
using var aliceEcdh = ECDiffieHellman.Create();
//Generate keys
bobEcdh.GenerateKey(eccCurve);
aliceEcdh.GenerateKey(eccCurve);
//Obtain shared secret
var sharedSecret = bobEcdh.DeriveSecretAgreement(aliceEcdh.PublicKey);

Alternative Designs

Add public method DeriveSecretAgreement only to specific ECDH classes (EcDiffieHellmanOpenSsl , ECDiffieHellmanAndroid.cs?, iOS?).

Better method name?

namespace System.Security.Cryptography;

 public abstract partial class ECDiffieHellman : ECAlgorithm
{

   public virtual byte[] DeriveSharedSecret(ECDiffieHellmanPublicKey otherPartyPublicKey)
   {
            throw DerivedClassMustOverride();   
   }
}

Risks

Shared raw secret (Truncate method in CNG BCryptDeriveKey) is available only on Windows 10?

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-System.Securityin-prThere is an active PR which will close this issue when it is merged

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions