Skip to content

Implement X25519DiffieHellman#127248

Merged
vcsjones merged 21 commits intodotnet:mainfrom
vcsjones:x25519-impl
Apr 24, 2026
Merged

Implement X25519DiffieHellman#127248
vcsjones merged 21 commits intodotnet:mainfrom
vcsjones:x25519-impl

Conversation

@vcsjones
Copy link
Copy Markdown
Member

This implements X25519 Diffie-Hellman on Windows 10+, Apple macOS, iOS / tvOS by way of CryptoKit, and OpenSSL.

What will be follow up pull requests

  1. Platform-specific implementations (X25519DiffieHellmanOpenSsl/Cng)
  2. Android implementation

Contributes to #126206

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

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

@vcsjones vcsjones added this to the 11.0.0 milestone Apr 21, 2026
@vcsjones vcsjones requested a review from bartonjs April 21, 2026 21:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the proposed X25519DiffieHellman API across Windows (CNG), Apple platforms (CryptoKit via Swift bindings), and OpenSSL, with extensive conformance/contract tests and supporting native interop glue.

Changes:

  • Adds new public System.Security.Cryptography.X25519DiffieHellman API (ref + implementation) with import/export (raw, SPKI, PKCS#8, PEM) and raw secret derivation.
  • Introduces platform-specific implementations (Windows/CNG, Apple/CryptoKit, OpenSSL) plus new native entrypoints/bindings for OpenSSL and Apple.
  • Adds a comprehensive test suite (vectors, key round-tripping, PEM/PKCS#8 behavior, supported vs not-supported gating) and factors shared PKCS#8 test validation into a helper.
Show a summary per file
File Description
src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_x25519.h Declares OpenSSL PAL entrypoints for X25519 import/export/generate.
src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_x25519.c Implements OpenSSL PAL wrappers for raw key import/export and keygen.
src/native/libs/System.Security.Cryptography.Native/opensslshim.h Adds OpenSSL shim bindings for raw X25519 key APIs.
src/native/libs/System.Security.Cryptography.Native/entrypoints.c Registers new OpenSSL-side native exports for X25519.
src/native/libs/System.Security.Cryptography.Native/CMakeLists.txt Includes the new OpenSSL X25519 PAL source in the native build.
src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift Adds CryptoKit-backed Swift entrypoints for X25519 operations.
src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h Declares Swift binding entrypoints for X25519 for P/Invoke table.
src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c Registers AppleCryptoNative X25519 entrypoints.
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanTests.cs Adds API-level tests for import/export, ASN.1 validation, PEM behavior.
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanTestData.cs Adds RFC 7748 vectors and encoded test blobs (SPKI/PKCS#8/etc.).
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanNotSupportedTests.cs Adds tests asserting PNSE behavior when X25519 isn’t supported.
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanKeyTests.cs Adds key roundtrip tests, scalar clamping preservation checks, vectors.
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanImplementationTests.cs Hooks base test suite to the public X25519DiffieHellman implementation.
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanContractTests.cs Adds contract tests for base-class behavior (dispose, buffer sizing, etc.).
src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanBaseTests.cs Adds shared behavioral tests (vectors, PEM/PKCS#8 export/import patterns).
src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj Wires new X25519 test files + shared PKCS#8 helper into test build.
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellmanImplementation.Windows.cs Implements X25519 over CNG (key import/export + secret agreement).
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellmanImplementation.OpenSsl.cs Implements X25519 over OpenSSL handles + PAL entrypoints.
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellmanImplementation.NotSupported.cs Provides the not-supported fallback implementation.
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellmanImplementation.Apple.cs Implements X25519 using AppleCrypto interop (CryptoKit).
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellman.cs Adds the public API surface and shared PKCS#8/SPKI/PEM glue logic.
src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj Adds new sources + platform interop files to the library build.
src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs Adds reference assembly surface for X25519DiffieHellman.
src/libraries/Common/tests/System/Security/Cryptography/Pkcs8TestHelpers.cs Introduces shared PKCS#8-encryption structure validation helper for tests.
src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs Refactors PKCS#8 encryption assertions to reuse Pkcs8TestHelpers.
src/libraries/Common/src/System/Security/Cryptography/Oids.cs Adds the X25519 OID constant for use in encoding/decoding.
src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeBCryptSecretHandle.cs Adds SafeHandle wrapper for BCrypt secret agreement handles.
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptSecretAgreement.cs Adds BCryptSecretAgreement P/Invoke wrapper returning SafeBCryptSecretHandle.
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroySecret.cs Adds BCryptDestroySecret P/Invoke for SafeHandle release.
src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDeriveKey.cs Adds BCryptDeriveKey P/Invoke wrapper used by X25519 derivation on Windows.
src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.X25519.cs Adds Unix/OpenSSL interop wrappers for X25519 PAL entrypoints.
src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X25519.cs Adds AppleCrypto interop + SafeX25519KeyHandle definition.
THIRD-PARTY-NOTICES.TXT Adds Wycheproof Apache-2.0 license notice for included test vectors.

Copilot's findings

Comments suppressed due to low confidence (2)

src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X25519DiffieHellman.cs:1276

  • ImportFromEncryptedPem(ReadOnlySpan, ReadOnlySpan) also doesn't call ThrowIfNotSupported() before attempting to parse the PEM. This can result in non-PlatformNotSupportedException failures when X25519 isn't available. Add an early ThrowIfNotSupported() for consistent platform-guard behavior.
        public static X25519DiffieHellman ImportFromEncryptedPem(ReadOnlySpan<char> source, ReadOnlySpan<byte> passwordBytes)
        {
            return PemKeyHelpers.ImportEncryptedFactoryPem<X25519DiffieHellman, byte>(
                source,
                passwordBytes,
                ImportEncryptedPkcs8PrivateKey);
        }

src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs:575

  • After switching AssertEncryptedPkcs8PrivateKeyContents to call Pkcs8TestHelpers, GetHashAlgorithmFromPbkdf2Params is now unused in this file. Please remove the dead method (and any now-unused usings) to avoid analyzer warnings / warnings-as-errors in test builds.
        private static void AssertEncryptedPkcs8PrivateKeyContents(PbeParameters pbeParameters, ReadOnlyMemory<byte> contents)
        {
            Pkcs8TestHelpers.AssertEncryptedPkcs8PrivateKeyContents(pbeParameters, contents);
        }

        private static HashAlgorithmName GetHashAlgorithmFromPbkdf2Params(in ValuePbkdf2Params pbkdf2Params)
        {
            return pbkdf2Params.Prf.Algorithm switch
            {
                "1.2.840.113549.2.7" => HashAlgorithmName.SHA1,
                "1.2.840.113549.2.9" => HashAlgorithmName.SHA256,
                "1.2.840.113549.2.10" => HashAlgorithmName.SHA384,
                "1.2.840.113549.2.11" => HashAlgorithmName.SHA512,
                string other => throw new XunitException($"Unknown hash algorithm OID '{other}'."),
            };
        }
  • Files reviewed: 33/33 changed files
  • Comments generated: 3

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 21, 2026 21:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Comments suppressed due to low confidence (1)

src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs:574

  • GetHashAlgorithmFromPbkdf2Params is now unused after moving the encrypted PKCS#8 parsing assertions into Pkcs8TestHelpers. If warnings are treated as errors for this test project, this will break the build. Remove the unused method (and any now-unneeded usings) or use it from Pkcs8TestHelpers instead of duplicating logic there.
        private static void AssertEncryptedPkcs8PrivateKeyContents(PbeParameters pbeParameters, ReadOnlyMemory<byte> contents)
        {
            Pkcs8TestHelpers.AssertEncryptedPkcs8PrivateKeyContents(pbeParameters, contents);
        }

        private static HashAlgorithmName GetHashAlgorithmFromPbkdf2Params(in ValuePbkdf2Params pbkdf2Params)
        {
            return pbkdf2Params.Prf.Algorithm switch
            {
                "1.2.840.113549.2.7" => HashAlgorithmName.SHA1,
                "1.2.840.113549.2.9" => HashAlgorithmName.SHA256,
                "1.2.840.113549.2.10" => HashAlgorithmName.SHA384,
                "1.2.840.113549.2.11" => HashAlgorithmName.SHA512,
                string other => throw new XunitException($"Unknown hash algorithm OID '{other}'."),
            };
  • Files reviewed: 33/33 changed files
  • Comments generated: 2

Copy link
Copy Markdown
Member

@bartonjs bartonjs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it through the main (non-native) implementation parts, stopped before the tests. I'll look more tomorrow.

Copilot AI review requested due to automatic review settings April 22, 2026 02:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 34/34 changed files
  • Comments generated: 1

Comment thread src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanBaseTests.cs Outdated
Copilot AI review requested due to automatic review settings April 22, 2026 21:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 34/34 changed files
  • Comments generated: 3

Comment thread src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift Outdated
Comment thread src/libraries/System.Security.Cryptography/tests/X25519DiffieHellmanBaseTests.cs Outdated
Copilot AI review requested due to automatic review settings April 24, 2026 03:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 35/35 changed files
  • Comments generated: 1

@vcsjones
Copy link
Copy Markdown
Member Author

vcsjones commented Apr 24, 2026

At this point it is probably worth me explaining what is going on with Windows public key imports.

Public keys can fall in to three buckets

  1. Normal public keys
  2. Low-order public keys
  3. Small-subgroup public keys

All three have an additional property of whether or not they are canonically encoded as well.

RFC 7748 has no restrictions on the keys that key imported. What RFC 7748 cares about is the result. If the shared secret is all zeros, this MAY be blocked.

For 2 and 3, CryptoKit and OpenSSL more or less do what the RFC says: let anything import and reject the all zero shared secret.

Windows, by default, does block key 2 and 3. However, this results in a different key import experience. We can pass a flag to BCrypt to permit 2 and 3.

Permitting 2 has an unintended side effect though: Windows does not explicitly block producing an all-zero shared secret. It handles this at key import time instead of at shared secret creation time.

However, we want to continue to allow 3: small subgroups are fine and legitimate in the case of X25519.

So, to unify platform behavior, Windows has some additional handling.

  1. We tell CNG at import time to allow 2 and 3, because there is only one flag. This practically lets all keys be imported.
  2. We block the all-zero shared secret that 2 permits. This brings the behavior in line with OpenSSL and CryptoKit - imports work fine, key agreement that produces all zeros fails.
  3. Small subgroup cases work now, consistent with OpenSSL and CryptoKit
  4. We have to manually canonicalize the public key by reducing it.

Copilot AI review requested due to automatic review settings April 24, 2026 18:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 35/35 changed files
  • Comments generated: 0 new

Copilot AI review requested due to automatic review settings April 24, 2026 18:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 35/35 changed files
  • Comments generated: 1

@vcsjones
Copy link
Copy Markdown
Member Author

/ba-g Build Analysis seems hung on runtime-report-green. Only one failure and it is tracked at #127406.

@vcsjones vcsjones merged commit 815febb into dotnet:main Apr 24, 2026
115 of 117 checks passed
@vcsjones vcsjones deleted the x25519-impl branch April 24, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants