Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ private static partial int AppleCryptoNative_X25519DeriveRawSecretAgreement(
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X25519DeriveRawSecretAgreementWithBytes")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
private static partial int AppleCryptoNative_X25519DeriveRawSecretAgreementWithBytes(
SafeX25519KeyHandle key,
ReadOnlySpan<byte> peerKey,
int peerKeyLength,
Span<byte> destination,
int destinationLength);

[LibraryImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X25519ExportPrivateKey")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
private static partial int AppleCryptoNative_X25519ExportPrivateKey(
Expand Down Expand Up @@ -71,6 +80,32 @@ internal static void X25519DeriveRawSecretAgreement(SafeX25519KeyHandle key, Saf
}
}

internal static void X25519DeriveRawSecretAgreementWithBytes(
SafeX25519KeyHandle key,
ReadOnlySpan<byte> peerKey,
Span<byte> destination)
{
const int Success = 1;
const int KeyDerivationFailed = 0;
int ret = AppleCryptoNative_X25519DeriveRawSecretAgreementWithBytes(
key,
peerKey,
peerKey.Length,
destination,
destination.Length);

switch (ret)
{
case Success:
return;
case KeyDerivationFailed:
throw new CryptographicException();
default:
Debug.Fail($"Unexpected result from {nameof(AppleCryptoNative_X25519DeriveRawSecretAgreementWithBytes)}: {ret}.");
throw new CryptographicException();
}
}

internal static void X25519ExportPrivateKey(SafeX25519KeyHandle key, Span<byte> destination)
{
const int Success = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ private static partial int X25519ExportPublicKey(
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X25519GenerateKey")]
private static partial SafeEvpPKeyHandle CryptoNative_X25519GenerateKey();

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X25519DeriveSecretAgreementWithPublicKey")]
private static partial int CryptoNative_X25519DeriveSecretAgreementWithPublicKey(
SafeEvpPKeyHandle key,
IntPtr extraHandle,
ReadOnlySpan<byte> peerKey,
int peerKeyLength,
Span<byte> secret,
uint secretLength);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X25519IsValidHandle")]
private static partial int CryptoNative_X25519IsValidHandle(
SafeEvpPKeyHandle key,
Expand Down Expand Up @@ -127,6 +136,32 @@ internal static SafeEvpPKeyHandle X25519ImportPrivateKey(ReadOnlySpan<byte> sour
return key;
}

internal static int X25519DeriveSecretAgreementWithPublicKey(
SafeEvpPKeyHandle key,
ReadOnlySpan<byte> peerKey,
Span<byte> destination)
{
Debug.Assert(key != null);
Debug.Assert(peerKey.Length == X25519DiffieHellman.PublicKeySizeInBytes);
Debug.Assert(destination.Length == X25519DiffieHellman.SecretAgreementSizeInBytes);

int written = CryptoNative_X25519DeriveSecretAgreementWithPublicKey(
key,
GetExtraHandle(key),
peerKey,
peerKey.Length,
destination,
(uint)destination.Length);

if (written <= 0)
{
Debug.Assert(written == 0);
throw CreateOpenSslCryptographicException();
}

return written;
}

internal static SafeEvpPKeyHandle X25519ImportPublicKey(ReadOnlySpan<byte> source)
{
SafeEvpPKeyHandle key = X25519ImportPublicKey(source, source.Length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3449,8 +3449,11 @@ public abstract partial class X25519DiffieHellman : System.IDisposable
protected X25519DiffieHellman() { }
public static bool IsSupported { get { throw null; } }
public byte[] DeriveRawSecretAgreement(System.Security.Cryptography.X25519DiffieHellman otherParty) { throw null; }
public byte[] DeriveRawSecretAgreement(byte[] otherPartyPublicKey) { throw null; }
public void DeriveRawSecretAgreement(System.Security.Cryptography.X25519DiffieHellman otherParty, System.Span<byte> destination) { }
public void DeriveRawSecretAgreement(System.ReadOnlySpan<byte> otherPartyPublicKey, System.Span<byte> destination) { }
protected abstract void DeriveRawSecretAgreementCore(System.Security.Cryptography.X25519DiffieHellman otherParty, System.Span<byte> destination);
protected abstract void DeriveRawSecretAgreementCore(System.ReadOnlySpan<byte> otherPartyPublicKey, System.Span<byte> destination);
Comment thread
vcsjones marked this conversation as resolved.
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public byte[] ExportPrivateKey() { throw null; }
Expand Down Expand Up @@ -3499,6 +3502,7 @@ public sealed partial class X25519DiffieHellmanCng : System.Security.Cryptograph
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
public X25519DiffieHellmanCng(System.Security.Cryptography.CngKey key) { }
protected override void DeriveRawSecretAgreementCore(System.Security.Cryptography.X25519DiffieHellman otherParty, System.Span<byte> destination) { }
protected override void DeriveRawSecretAgreementCore(System.ReadOnlySpan<byte> otherPartyPublicKey, System.Span<byte> destination) { }
protected override void Dispose(bool disposing) { }
protected override void ExportPrivateKeyCore(System.Span<byte> destination) { }
protected override void ExportPublicKeyCore(System.Span<byte> destination) { }
Expand All @@ -3515,6 +3519,7 @@ public sealed partial class X25519DiffieHellmanOpenSsl : System.Security.Cryptog
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("windows")]
public X25519DiffieHellmanOpenSsl(System.Security.Cryptography.SafeEvpPKeyHandle pkeyHandle) { }
protected override void DeriveRawSecretAgreementCore(System.Security.Cryptography.X25519DiffieHellman otherParty, System.Span<byte> destination) { }
protected override void DeriveRawSecretAgreementCore(System.ReadOnlySpan<byte> otherPartyPublicKey, System.Span<byte> destination) { }
protected override void Dispose(bool disposing) { }
public System.Security.Cryptography.SafeEvpPKeyHandle DuplicateKeyHandle() { throw null; }
protected override void ExportPrivateKeyCore(System.Span<byte> destination) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,11 @@ protected override unsafe partial void DeriveRawSecretAgreementCore(X25519Diffie
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng);
}

protected override partial void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng);
}

protected override partial void ExportPrivateKeyCore(Span<byte> destination)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,40 @@ public byte[] DeriveRawSecretAgreement(X25519DiffieHellman otherParty)
return buffer;
}

/// <summary>
/// Derives a raw secret agreement with the other party's public key.
/// </summary>
/// <param name="otherPartyPublicKey">
/// The other party's public key.
/// </param>
/// <returns>
/// The secret agreement.
/// </returns>
/// <remarks>
/// The raw secret agreement value is expected to be used as input into a Key Derivation Function,
/// and not used directly as key material.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// <paramref name="otherPartyPublicKey" /> is <see langword="null" />.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="otherPartyPublicKey" /> has a length that is not <see cref="PublicKeySizeInBytes" />.
/// </exception>
/// <exception cref="CryptographicException">
/// An error occurred during the secret agreement derivation.
/// </exception>
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
public byte[] DeriveRawSecretAgreement(byte[] otherPartyPublicKey)
{
ArgumentNullException.ThrowIfNull(otherPartyPublicKey);
ThrowIfPublicKeyWrongSize(otherPartyPublicKey, nameof(otherPartyPublicKey));
ThrowIfDisposed();

byte[] buffer = new byte[SecretAgreementSizeInBytes];
DeriveRawSecretAgreementCore(otherPartyPublicKey, buffer);
return buffer;
}

/// <summary>
/// Derives a raw secret agreement with the other party's key, writing it into the provided buffer.
/// </summary>
Expand Down Expand Up @@ -120,6 +154,43 @@ public void DeriveRawSecretAgreement(X25519DiffieHellman otherParty, Span<byte>
DeriveRawSecretAgreementCore(otherParty, destination);
}

/// <summary>
/// Derives a raw secret agreement with the other party's public key, writing it into the provided buffer.
/// </summary>
/// <param name="otherPartyPublicKey">
/// The other party's public key.
/// </param>
/// <param name="destination">
/// The buffer to receive the secret agreement.
/// </param>
/// <remarks>
/// The raw secret agreement value is expected to be used as input into a Key Derivation Function,
/// and not used directly as key material.
/// </remarks>
/// <exception cref="ArgumentException">
/// <para><paramref name="otherPartyPublicKey" /> has a length that is not <see cref="PublicKeySizeInBytes" />.</para>
/// <para>-or-</para>
/// <para><paramref name="destination" /> is the incorrect length to receive the secret agreement.</para>
/// </exception>
/// <exception cref="CryptographicException">
/// An error occurred during the secret agreement derivation.
/// </exception>
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
public void DeriveRawSecretAgreement(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
ThrowIfPublicKeyWrongSize(otherPartyPublicKey, nameof(otherPartyPublicKey));

if (destination.Length != SecretAgreementSizeInBytes)
{
throw new ArgumentException(
SR.Format(SR.Argument_DestinationImprecise, SecretAgreementSizeInBytes),
nameof(destination));
}

ThrowIfDisposed();
DeriveRawSecretAgreementCore(otherPartyPublicKey, destination);
}

/// <summary>
/// Generates a new X25519 Diffie-Hellman key.
/// </summary>
Expand Down Expand Up @@ -736,6 +807,21 @@ public string ExportEncryptedPkcs8PrivateKeyPem(string password, PbeParameters p
/// </exception>
protected abstract void DeriveRawSecretAgreementCore(X25519DiffieHellman otherParty, Span<byte> destination);

/// <summary>
/// When overridden in a derived class, derives a raw secret agreement with the other party's public key,
/// writing it into the provided buffer.
/// </summary>
/// <param name="otherPartyPublicKey">
/// The other party's public key.
/// </param>
/// <param name="destination">
/// The buffer to receive the secret agreement.
/// </param>
/// <exception cref="CryptographicException">
/// An error occurred during the secret agreement derivation.
/// </exception>
protected abstract void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination);

/// <summary>
/// When overridden in a derived class, exports the private key into the provided buffer.
/// </summary>
Expand Down Expand Up @@ -1485,6 +1571,12 @@ private protected void ThrowIfDisposed()
ObjectDisposedException.ThrowIf(_disposed, typeof(X25519DiffieHellman));
}

private static void ThrowIfPublicKeyWrongSize(ReadOnlySpan<byte> publicKey, string paramName)
{
if (publicKey.Length != PublicKeySizeInBytes)
throw new ArgumentException(SR.Argument_PublicKeyWrongSizeForAlgorithm, paramName);
}

private protected static void ThrowIfNotSupported()
{
if (!IsSupported)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,28 @@ protected override unsafe partial void DeriveRawSecretAgreementCore(X25519Diffie
{
// We intentionally don't special case otherParty being an instance of X25519DiffieHellmanCng and always
// export the public key into the current instance's provider.
scoped Span<byte> publicKeyBuffer;
Span<byte> publicKeyBytes = stackalloc byte[PublicKeySizeInBytes];
otherParty.ExportPublicKey(publicKeyBytes);
DeriveRawSecretAgreementWithPublicKey(publicKeyBytes, destination);
}

protected override partial void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
Debug.Assert(otherPartyPublicKey.Length == PublicKeySizeInBytes);
Debug.Assert(destination.Length == SecretAgreementSizeInBytes);
DeriveRawSecretAgreementWithPublicKey(otherPartyPublicKey, destination);
}

private void DeriveRawSecretAgreementWithPublicKey(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
scoped Span<byte> reducedPublicKey;

unsafe
{
publicKeyBuffer = stackalloc byte[PublicKeySizeInBytes * 2];
reducedPublicKey = stackalloc byte[PublicKeySizeInBytes];
}

Span<byte> publicKeyBytes = publicKeyBuffer.Slice(0, PublicKeySizeInBytes);
Span<byte> reducedPublicKey = publicKeyBuffer.Slice(PublicKeySizeInBytes, PublicKeySizeInBytes);
otherParty.ExportPublicKey(publicKeyBytes);
X25519WindowsHelpers.ReducePublicKey(publicKeyBytes, reducedPublicKey);
X25519WindowsHelpers.ReducePublicKey(otherPartyPublicKey, reducedPublicKey);

// CNG does not permit cross-provider key agreements. Import the public key in to the same provider
// as the current key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public sealed partial class X25519DiffieHellmanCng : X25519DiffieHellman
/// <inheritdoc/>
protected override unsafe partial void DeriveRawSecretAgreementCore(X25519DiffieHellman otherParty, Span<byte> destination);

/// <inheritdoc/>
protected override partial void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination);

/// <inheritdoc/>
protected override partial void ExportPublicKeyCore(Span<byte> destination);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ private X25519DiffieHellmanImplementation(SafeX25519KeyHandle key, bool hasPriva
_hasPrivate = hasPrivate;
}

protected override unsafe void DeriveRawSecretAgreementCore(X25519DiffieHellman otherParty, Span<byte> destination)
protected override void DeriveRawSecretAgreementCore(X25519DiffieHellman otherParty, Span<byte> destination)
{
Debug.Assert(destination.Length == SecretAgreementSizeInBytes);
ThrowIfPrivateNeeded();

if (otherParty is X25519DiffieHellmanImplementation x25519impl)
Expand All @@ -29,16 +30,23 @@ protected override unsafe void DeriveRawSecretAgreementCore(X25519DiffieHellman
}
else
{
Span<byte> publicKeyBuffer = stackalloc byte[PublicKeySizeInBytes];
otherParty.ExportPublicKey(publicKeyBuffer);

using (SafeX25519KeyHandle publicKey = Interop.AppleCrypto.X25519ImportPublicKey(publicKeyBuffer))
unsafe
{
Interop.AppleCrypto.X25519DeriveRawSecretAgreement(_key, publicKey, destination);
Span<byte> publicKeyBuffer = stackalloc byte[PublicKeySizeInBytes];
otherParty.ExportPublicKey(publicKeyBuffer);
Interop.AppleCrypto.X25519DeriveRawSecretAgreementWithBytes(_key, publicKeyBuffer, destination);
}
}
}

protected override void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
Debug.Assert(otherPartyPublicKey.Length == PublicKeySizeInBytes);
Debug.Assert(destination.Length == SecretAgreementSizeInBytes);
ThrowIfPrivateNeeded();
Interop.AppleCrypto.X25519DeriveRawSecretAgreementWithBytes(_key, otherPartyPublicKey, destination);
}

protected override void ExportPrivateKeyCore(Span<byte> destination)
{
ThrowIfPrivateNeeded();
Expand Down Expand Up @@ -92,5 +100,6 @@ private void ThrowIfPrivateNeeded()
if (!_hasPrivate)
throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ protected override void DeriveRawSecretAgreementCore(X25519DiffieHellman otherPa
throw new PlatformNotSupportedException();
}

protected override void DeriveRawSecretAgreementCore(ReadOnlySpan<byte> otherPartyPublicKey, Span<byte> destination)
{
Debug.Fail("Caller should have checked platform availability.");
throw new PlatformNotSupportedException();
}

protected override void ExportPrivateKeyCore(Span<byte> destination)
{
Debug.Fail("Caller should have checked platform availability.");
Expand Down
Loading
Loading