Skip to content

Latest commit

 

History

History
236 lines (185 loc) · 11.8 KB

File metadata and controls

236 lines (185 loc) · 11.8 KB

A120 - Post-Quantum Cryptography

  • Author(s): @gtcooke94
  • Approver: markdroth, ejona86, easwars, matthewstevenson88, dfawley
  • Implemented in: C++, Go, Java
  • Last updated: 2026-06-05
  • Discussion at: (filled after thread exists)

Abstract

The industry's current best estimates are that a cryptographically-relevant quantum computer (CRQC) will be available by Eo2028. Prior to recent quantum computing advances announced on March 30th, 2026, and March 31st, 2026, the industry consensus was that the date for a CRQC would be 2030+, thus the recent and sudden urgency. Anyone harvesting encrypted data for store-now-decrypt-later (SNDL) attacks will be able to break the current non-quantum ciphers in ~2.5 years. gRPC must change its defaults to leverage post-quantum cryptography (PQC) as soon as possible. Particularly, in the immediate future, gRPC must ensure post-quantum confidentiality in its SSL/TLS stacks by preferring the X25519-MLKEM768 key exchange algorithm.

Background

Maintainers of the individual pieces of the general TLS ecosystem are in consensus that moving to PQC is of the highest priority. The general consensus is to secure quickly and optimize for performance later.

Performance concerns are the primary source of anticipated pushback. However, we align with the broader ecosystem: prioritizing rapid PQC readiness is critical. Performance-sensitive users that cannot take this approach can choose to opt-out by configuring their ciphers. In addition, gRPC SSL/TLS users are not historically sensitive to ~600 μs handshake latency delta. For example, gRPC-C++ does not enable TLS resumption by default, which would save much more than that.

Post-quantum authenticity is not susceptible to store-now-decrypt-later attacks so is out-of-scope for the sudden urgency.

Not all of gRPC's TLS stack enables users to configure their own key exchange groups. This is a loose prerequisite to changing the default behavior for gRPC, as we want users to be able to opt-out of post-quantum confidentiality if they desire.

Proposal

The gRPC TLS stacks must prefer the X25519-MLKEM768 key exchange group over non-PQC-safe groups. The current defaults vary per language. Notably, this is a prefer, not a require, so we can allow for graceful fallback from a PQC-safe cipher to the previous defaults (e.g. to allow interop with old applications). In the TLS handshake, key exchange groups are negotiated as follows, per RFC8446: the client sends an ordered list of key exchange groups that it supports, and the server selects a key exchange group from that list. The RFC does not specify whether the client or server preference should "win". In BoringSSL, the server selects the first item in the client list that it supports. However, in Go, the server preferences are preferred.

In the case where the security library is already preferring PQC, we will not take over the defaults. In the case that the underlying security libraries are not already preferring PQC, gRPC will take an opinionated default of {X25519-MLKEM768, X25519, P-256, P-384, P-521}. MLKEM1024 will be added to this list when widely supported.

Further, each gRPC language implementation will have a way to set key exchange groups.

C++

In OpenSSL < 3 (and thus BoringSSL), if no key exchange groups are set, gRPC currently defaults as follows:

Library / Version Key Exchange Groups Source / Notes
BoringSSL (1.1.1) P-256 gRPC Default
OpenSSL < 3 P-256 gRPC Default
3 <= OpenSSL < 3.5 {X25519, P-256, X448, ... many more ...} OpenSSL Default
OpenSSL >= 3.5 {X25519-MLKEM768, X25519, P-256, X448, P-384, P-521, ffdhe2048, ffdhe3072} OpenSSL Default

The following table describes what the default behavior should be for each version. X25519-MLKEM768 is only supported in BoringSSL and OpenSSL 3.5+.

Library / Version Recommended Configuration / Behavior
BoringSSL (1.1.1) {X25519-MLKEM768, X25519, P-256, P-384, P-521} with SSL_CTX_set1_groups_list (Until the better defaults in BoringSSL)
OpenSSL < 3 {X25519, P-256, P-384, P-521} with SSL_CTX_set1_groups/SSL_CTX_set1_curves
OpenSSL 3+ (Use OpenSSL defaults):
< 3.5 {X25519, P-256, P-384, P-521},
3.5+ {X25519-MLKEM768, …}

In addition, the negotiated key exchange group will be made available on the AuthContext and the TlsCustomVerificationCheckRequest.

C-Core API Changes:

typedef struct grpc_tls_custom_verification_check_request {
  // ...
  const char *negotiated_key_exchange_group;
  // ...
}

#define GRPC_SSL_NEGOTIATED_KEY_EXCHANGE_GROUP_PROPERTY_NAME \
  "ssl_negotiated_key_exchange_group"

C++ API Changes:

class TlsCustomVerificationCheckRequest {
 public:
  // ...
  grpc::string_ref negotiated_key_exchange_group() const;
  // ...
}

Go

In Golang, the crypto/tls library is part of the core language, and as of Go 1.24 the X25519-MLKEM768 hybrid cipher is already the default. As of the writing of this document, gRPC-Go supports Go 1.25+, so preferring the PQC hybrid cipher by default is a no-op.

Users can configure their own preference via tls.Config.CurvePreferences. However, advancedtls does not currently support the user passing through CurvePreferences.

This means gRPC-Go with advancedtls is fully dependent on the Golang defaults. Otherwise, users have direct access to the tls.Config and can set the CurvePreference.

We will add a passthrough setting in advancedtls for the user to explicitly configure CurvePreferences. It will be advancedtls.Options.CurvePreferences and will be a simple passthrough to the tls.Config.CurvePreferences.

Go version Defaults
Go < 1.24 {X25519, P-256, P-384, P-521}
1.24 <= Go < 1.26 {X25519-MLKEM768, X25519, P-256, P-384, P-521}
Go >= 1.26 {X25519-MLKEM768, P-256-MLKEM768, P-384-MLKEM1024, X25519, P-256, P-384, P-521}

Java

Similar to Go, gRPC-Java does not currently make opinionated preferences on the key exchange algorithm, and the defaults are dependent upon the security provider used. The providers have significant differences and this creates confusion for users. Further, X25519-MLKEM768 is not supported in all Java versions, nor in all security providers. We propose that we update gRPC-Java to use netty-tcnative 4.2 (which uses BoringSSL) to cover the most important use cases. Otherwise, OpenJDK 27 is the first version in which X25519-MLKEM768 is supported, so we are constrained by the language.

  • netty-tcnative 4.1 (w/ BoringSSL) - {X25519, P-256, P-384, P-521}
  • netty-tcnative 4.2 (w/ BoringSSL) - {X25519-MLKEM768, X25519, P-256, P-384, P-521}
  • OpenJDK 27+ post-quantum is supported by default - the exact list is {X25519-MLKEM768, X25519, P-256, P-384, P-521, X448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192}
  • Conscrypt does not support X25519-MLKEM768 (no recent build with newer BoringSSL)

In Java 21+, the SSLParameters.setNamedGroups method will allow gRPC to create a passthrough API that allows a user to configure their algorithms directly.

A field will be added to TlsChannelCredentials, and a new TlsChannelCredentials.Feature will be defined to support setting the key exchange groups. This will pass through to SSLParameters.setNamedGroups.

Temporary environment variable protection

No temporary environment variable protection is required. This change prefers X25519-MLKEM768 by default but allows graceful fallback, and users can opt-out entirely by configuring their ciphersuites or curve preferences.

Rationale

Prioritizing rapid PQC readiness is critical as SNDL attacks present an immediate threat. Preferring X25519-MLKEM768 provides hybrid post-quantum confidentiality while preserving decades of classical security guarantees and allowing graceful fallback for legacy interop.

Why X25519-MLKEM768?

X25519-MLKEM768 is a hybrid algorithm. Specifically, it combines the classical X25519 cipher and the post-quantum MLKEM768 cipher. Think of the two ciphers as two separate "locks" on the data - an adversary has to break both locks individually to capture the data. We have decades of confidence in classical cryptography protecting against normal computing, so we keep that and add post-quantum protections on top. In this way, gRPC traffic is still protected against potentially unknown classical attacks against the post-quantum MLKEM768 cipher. This choice is consistent with what the rest of the industry is doing (e.g. across the broader TLS ecosystem and the Web).

As to why we select the X25519-MLKEM768 combination specifically, simply put, it is ready, whereas alternatives (like X25519-MLKEM1024 or MLKEM1024) are not yet widely available in TLS libraries at present. We plan to add support for these as they become available, and in many cases they will become the new defaults of the crypto libraries.

C++ Performance Comparison

The C++ gRPC benchmark suite evaluates connection setup overhead:

Benchmark Real Time / Op CPU Time / Op Instructions / Op Peak Mem (Bytes) Allocs / Op
BM_TlsConnection (Default) 965.7 µs ± 2% 1.291 ms ± 6% 96.15k ± 1% 741.1k ± 9% 2.715k ± 5%
BM_TlsConnection_X25519 (Forced X25519) 931.0 µs ± 2% 1.258 ms ± 2% 96.12k ± 0% 741.2k ± 9% 2.697k ± 4%
BM_TlsConnection_X25519_MLKEM768 (Hybrid) 1.042 ms ± 1% 1.368 ms ± 2% 96.16k ± 0% 741.6k ± 9% 2.706k ± 4%
BM_MtlsConnection (Default) 1.171 ms ± 1% 1.513 ms ± 3% 96.17k ± 0% 751.9k ± 2% 3.077k ± 0%
BM_MtlsConnection_X25519 (Forced X25519) 1.138 ms ± 2% 1.474 ms ± 1% 96.18k ± 0% 755.6k ± 8% 3.059k ± 3%
BM_MtlsConnection_X25519_MLKEM768 (Hybrid) 1.248 ms ± 1% 1.580 ms ± 1% 96.25k ± 0% 754.6k ± 9% 3.064k ± 3%

Go Performance Comparison

The Go gRPC credentials benchmark evaluates connection latency and allocation overhead:

Benchmark Latency / Connection Delta vs. Std TLS (%) Allocations / Connection Memory (KiB) / Connection
Standard TLS (BenchmarkTlsConnection) 819.9 µs ± 0% - 1,681 ± 0% 197.5 KiB ± 0%
Standard mTLS (BenchmarkMtlsConnection) 1.013 ms ± 0% +23.5% 1,901 ± 0% 223.0 KiB ± 0%
PQ TLS (BenchmarkTlsConnection_X25519_MLKEM768) 1.060 ms ± 0% +29.3% 1,700 ± 0% 243.7 KiB ± 0%
PQ mTLS (BenchmarkMtlsConnection_X25519_MLKEM768) 1.248 ms ± 0% +52.2% 1,918 ± 0% 266.5 KiB ± 0%

Java Performance Comparison

The Java gRPC credentials benchmark evaluates connection setup latency:

Benchmark Latency / Connection (us) Delta vs. Std TLS (%)
Standard TLS (BenchmarkTlsConnection) 820.92 µs -
PQ TLS (BenchmarkTlsConnection_X25519_MLKEM768) 931.81 µs +13.5% (+110.89 µs)
Standard mTLS (BenchmarkMtlsConnection) 1,168.98 µs +42.4% (+348.06 µs)
PQ mTLS (BenchmarkMtlsConnection_X25519_MLKEM768) 1,287.70 µs +56.8% (+466.78 µs)