private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher) { if (!(otherPartyPublicKey is ECDiffieHellmanSecurityTransformsPublicKey secTransPubKey)) { secTransPubKey = new ECDiffieHellmanSecurityTransformsPublicKey(otherPartyPublicKey.ExportParameters()); } try { SafeSecKeyRefHandle otherPublic = secTransPubKey.KeyHandle; if (Interop.AppleCrypto.EccGetKeySizeInBits(otherPublic) != KeySize) { throw new ArgumentException( SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey)); } SafeSecKeyRefHandle?thisPrivate = GetKeys().PrivateKey; if (thisPrivate == null) { throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } // Since Apple only supports secp256r1, secp384r1, and secp521r1; and 521 fits in // 66 bytes ((521 + 7) / 8), the Span path will always succeed. Span <byte> secretSpan = stackalloc byte[66]; byte[]? secret = Interop.AppleCrypto.EcdhKeyAgree( thisPrivate, otherPublic, secretSpan, out int bytesWritten); // Either we wrote to the span or we returned an array, but not both, and not neither. // ("neither" would have thrown) Debug.Assert( (bytesWritten == 0) != (secret == null), $"bytesWritten={bytesWritten}, (secret==null)={secret == null}"); if (hasher == null) { return(secret ?? secretSpan.Slice(0, bytesWritten).ToArray()); } if (secret == null) { hasher.AppendData(secretSpan.Slice(0, bytesWritten)); } else { hasher.AppendData(secret); Array.Clear(secret, 0, secret.Length); } return(null); } finally { if (!ReferenceEquals(otherPartyPublicKey, secTransPubKey)) { secTransPubKey.Dispose(); } } }
/// <summary> /// Get the secret agreement generated between two parties /// </summary> private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher) { Debug.Assert(otherPartyPublicKey != null); // Ensure that this ECDH object contains a private key by attempting a parameter export // which will throw an OpenSslCryptoException if no private key is available ECParameters thisKeyExplicit = ExportExplicitParameters(true); bool thisIsNamed = Interop.Crypto.EcKeyHasCurveName(_key.Value); ECDiffieHellmanOpenSslPublicKey?otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey; bool disposeOtherKey = false; if (otherKey == null) { disposeOtherKey = true; ECParameters otherParameters = thisIsNamed ? otherPartyPublicKey.ExportParameters() : otherPartyPublicKey.ExportExplicitParameters(); otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters); } bool otherIsNamed = otherKey.HasCurveName; SafeEvpPKeyHandle?ourKey = null; SafeEvpPKeyHandle?theirKey = null; byte[]? rented = null; int secretLength = 0; try { if (otherKey.KeySize != KeySize) { throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey)); } if (otherIsNamed == thisIsNamed) { ourKey = _key.UpRefKeyHandle(); theirKey = otherKey.DuplicateKeyHandle(); } else if (otherIsNamed) { ourKey = _key.UpRefKeyHandle(); using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters())) { theirKey = tmp.UpRefKeyHandle(); } } else { using (ECOpenSsl tmp = new ECOpenSsl(thisKeyExplicit)) { ourKey = tmp.UpRefKeyHandle(); } theirKey = otherKey.DuplicateKeyHandle(); } using (SafeEvpPKeyCtxHandle ctx = Interop.Crypto.EvpPKeyCtxCreate(ourKey, theirKey, out uint secretLengthU)) { if (ctx == null || ctx.IsInvalid || secretLengthU == 0 || secretLengthU > int.MaxValue) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } secretLength = (int)secretLengthU; // Indicate that secret can hold stackallocs from nested scopes Span <byte> secret = stackalloc byte[0]; // Arbitrary limit. But it covers secp521r1, which is the biggest common case. const int StackAllocMax = 66; if (secretLength > StackAllocMax) { rented = CryptoPool.Rent(secretLength); secret = new Span <byte>(rented, 0, secretLength); } else { secret = stackalloc byte[secretLength]; } Interop.Crypto.EvpPKeyDeriveSecretAgreement(ctx, secret); if (hasher == null) { return(secret.ToArray()); } else { hasher.AppendData(secret); return(null); } } } finally { theirKey?.Dispose(); ourKey?.Dispose(); if (disposeOtherKey) { otherKey.Dispose(); } if (rented != null) { CryptoPool.Return(rented, secretLength); } } }
/// <summary> /// Get the secret agreement generated between two parties /// </summary> private byte[]? DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash?hasher) { Debug.Assert(otherPartyPublicKey != null); // Ensure that this ECDH object contains a private key by attempting a parameter export // which will throw an OpenSslCryptoException if no private key is available ECParameters thisKeyExplicit = ExportExplicitParameters(true); bool thisIsNamed = Interop.AndroidCrypto.EcKeyHasCurveName(_key.Value); ECDiffieHellmanAndroidPublicKey?otherKey = otherPartyPublicKey as ECDiffieHellmanAndroidPublicKey; bool disposeOtherKey = false; if (otherKey == null) { disposeOtherKey = true; ECParameters otherParameters = thisIsNamed ? otherPartyPublicKey.ExportParameters() : otherPartyPublicKey.ExportExplicitParameters(); otherKey = new ECDiffieHellmanAndroidPublicKey(otherParameters); } bool otherIsNamed = otherKey.HasCurveName; SafeEcKeyHandle?ourKey = null; SafeEcKeyHandle?theirKey = null; byte[]? rented = null; // Calculate secretLength in bytes. int secretLength = AsymmetricAlgorithmHelpers.BitsToBytes(KeySize); try { if (otherKey.KeySize != KeySize) { throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey)); } if (otherIsNamed == thisIsNamed) { ourKey = _key.UpRefKeyHandle(); theirKey = otherKey.DuplicateKeyHandle(); } else if (otherIsNamed) { ourKey = _key.UpRefKeyHandle(); using (ECAndroid tmp = new ECAndroid(otherKey.ExportExplicitParameters())) { theirKey = tmp.UpRefKeyHandle(); } } else { using (ECAndroid tmp = new ECAndroid(thisKeyExplicit)) { ourKey = tmp.UpRefKeyHandle(); } theirKey = otherKey.DuplicateKeyHandle(); } // Indicate that secret can hold stackallocs from nested scopes Span <byte> secret = stackalloc byte[0]; // Arbitrary limit. But it covers secp521r1, which is the biggest common case. const int StackAllocMax = 66; if (secretLength > StackAllocMax) { rented = CryptoPool.Rent(secretLength); secret = new Span <byte>(rented, 0, secretLength); } else { secret = stackalloc byte[secretLength]; } if (!Interop.AndroidCrypto.EcdhDeriveKey(ourKey, theirKey, secret, out int usedBufferLength)) { throw new CryptographicException(); } Debug.Assert(secretLength == usedBufferLength, $"Expected secret length {secretLength} does not match actual secret length {usedBufferLength}."); if (hasher == null) { return(secret.ToArray()); } else { hasher.AppendData(secret); return(null); } } finally { theirKey?.Dispose(); ourKey?.Dispose(); if (disposeOtherKey) { otherKey.Dispose(); } if (rented != null) { CryptoPool.Return(rented, secretLength); } } }