|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Runtime.Versioning;
namespace System.Security.Cryptography
{
/// <summary>
/// Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
/// </summary>
public sealed partial class ECDiffieHellmanCng : ECDiffieHellman
{
private CngAlgorithmCore _core = new CngAlgorithmCore(typeof(ECDiffieHellmanCng)) { DefaultKeyType = CngAlgorithm.ECDiffieHellman };
private CngAlgorithm _hashAlgorithm = CngAlgorithm.Sha256;
private ECDiffieHellmanKeyDerivationFunction _kdf = ECDiffieHellmanKeyDerivationFunction.Hash;
private byte[]? _hmacKey;
private byte[]? _label;
private byte[]? _secretAppend;
private byte[]? _secretPrepend;
private byte[]? _seed;
[SupportedOSPlatform("windows")]
public ECDiffieHellmanCng(CngKey key)
{
ArgumentNullException.ThrowIfNull(key);
if (key.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman)
throw new ArgumentException(SR.Cryptography_ArgECDHRequiresECDHKey, nameof(key));
Key = CngAlgorithmCore.Duplicate(key);
}
[SupportedOSPlatform("windows")]
internal ECDiffieHellmanCng(CngKey key, bool transferOwnership)
{
Debug.Assert(key is not null);
Debug.Assert(key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
Debug.Assert(transferOwnership);
Key = key;
}
/// <summary>
/// Hash algorithm used with the Hash and HMAC KDFs
/// </summary>
public CngAlgorithm HashAlgorithm
{
get
{
return _hashAlgorithm;
}
set
{
ArgumentNullException.ThrowIfNull(value);
_hashAlgorithm = value;
}
}
/// <summary>
/// KDF used to transform the secret agreement into key material
/// </summary>
public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction
{
get
{
return _kdf;
}
set
{
if (value < ECDiffieHellmanKeyDerivationFunction.Hash || value > ECDiffieHellmanKeyDerivationFunction.Tls)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_kdf = value;
}
}
/// <summary>
/// Key used with the HMAC KDF
/// </summary>
public byte[]? HmacKey
{
get { return _hmacKey; }
set { _hmacKey = value; }
}
/// <summary>
/// Label bytes used for the TLS KDF
/// </summary>
public byte[]? Label
{
get { return _label; }
set { _label = value; }
}
/// <summary>
/// Bytes to append to the raw secret agreement before processing by the KDF
/// </summary>
public byte[]? SecretAppend
{
get { return _secretAppend; }
set { _secretAppend = value; }
}
/// <summary>
/// Bytes to prepend to the raw secret agreement before processing by the KDF
/// </summary>
public byte[]? SecretPrepend
{
get { return _secretPrepend; }
set { _secretPrepend = value; }
}
/// <summary>
/// Seed bytes used for the TLS KDF
/// </summary>
public byte[]? Seed
{
get { return _seed; }
set { _seed = value; }
}
/// <summary>
/// Use the secret agreement as the HMAC key rather than supplying a separate one
/// </summary>
public bool UseSecretAgreementAsHmacKey
{
get { return HmacKey == null; }
}
protected override void Dispose(bool disposing)
{
_core.Dispose();
}
private void ThrowIfDisposed()
{
_core.ThrowIfDisposed();
}
private void DisposeKey()
{
_core.DisposeKey();
}
internal string? GetCurveName(out string? oidValue)
{
return Key.GetCurveName(out oidValue);
}
private void ImportFullKeyBlob(byte[] ecfullKeyBlob, bool includePrivateParameters)
{
CngKey newKey = ECCng.ImportFullKeyBlob(ecfullKeyBlob, includePrivateParameters);
try
{
Key = newKey;
}
catch
{
newKey.Dispose();
throw;
}
}
private void ImportKeyBlob(byte[] ecfullKeyBlob, string curveName, bool includePrivateParameters)
{
CngKey newKey = ECCng.ImportKeyBlob(ecfullKeyBlob, curveName, includePrivateParameters);
try
{
Key = newKey;
}
catch
{
newKey.Dispose();
throw;
}
}
private byte[] ExportKeyBlob(bool includePrivateParameters)
{
return ECCng.ExportKeyBlob(Key, includePrivateParameters);
}
private byte[] ExportFullKeyBlob(bool includePrivateParameters)
{
return ECCng.ExportFullKeyBlob(Key, includePrivateParameters);
}
private void AcceptImport(CngPkcs8.Pkcs8Response response)
{
try
{
Key = response.Key;
}
catch
{
response.FreeKey();
throw;
}
}
public override bool TryExportPkcs8PrivateKey(Span<byte> destination, out int bytesWritten)
{
bool encryptedOnlyExport = CngPkcs8.AllowsOnlyEncryptedExport(Key);
if (encryptedOnlyExport)
{
const string TemporaryExportPassword = "DotnetExportPhrase";
byte[] exported = ExportEncryptedPkcs8(TemporaryExportPassword, 1);
EccKeyFormatHelper.ReadEncryptedPkcs8(
exported,
TemporaryExportPassword,
out _,
out ECParameters ecParameters);
return EccKeyFormatHelper.WritePkcs8PrivateKey(ecParameters).TryEncode(destination, out bytesWritten);
}
return Key.TryExportKeyBlob(
Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
destination,
out bytesWritten);
}
private byte[] ExportEncryptedPkcs8(ReadOnlySpan<char> pkcs8Password, int kdfCount)
{
return Key.ExportPkcs8KeyBlob(pkcs8Password, kdfCount);
}
private bool TryExportEncryptedPkcs8(
ReadOnlySpan<char> pkcs8Password,
int kdfCount,
Span<byte> destination,
out int bytesWritten)
{
return Key.TryExportPkcs8KeyBlob(
pkcs8Password,
kdfCount,
destination,
out bytesWritten);
}
}
}
|