← Knowledge base

Google Cloud

Is PQC enabled? — quick check

macOS / Linux

# 1) No-dependency check — identify this machine first.
uname -a 2>/dev/null || true

# 2) Dependency check — prompt before installing anything.
if ! command -v openssl >/dev/null 2>&1; then
  echo 'OpenSSL was not found. A local PQC proof needs OpenSSL 3.5+.'
  printf 'Install OpenSSL now? [y/N] '
  read answer
  case "$answer" in
    [Yy]*)
      if command -v brew >/dev/null 2>&1; then brew install openssl@3
      elif command -v apt-get >/dev/null 2>&1; then sudo apt-get update && sudo apt-get install -y openssl
      elif command -v dnf >/dev/null 2>&1; then sudo dnf install -y openssl
      elif command -v yum >/dev/null 2>&1; then sudo yum install -y openssl
      else echo 'No supported package manager found. Install OpenSSL 3.5+ and retry.'; exit 1
      fi ;;
    *) echo 'Install OpenSSL 3.5+ and retry for a local PQC proof.'; exit 1 ;;
  esac
fi

OPENSSL=openssl
if command -v brew >/dev/null 2>&1; then
  BREW_OPENSSL="$(brew --prefix openssl@3 2>/dev/null)/bin/openssl"
  [ -x "$BREW_OPENSSL" ] && OPENSSL="$BREW_OPENSSL"
fi

$OPENSSL version
if ! $OPENSSL list -tls-groups 2>/dev/null | grep -qiE 'X25519MLKEM768|MLKEM|Kyber'; then
  echo 'This OpenSSL does not advertise ML-KEM groups. Upgrade to OpenSSL 3.5+ or load oqsprovider, then retry.'
  exit 1
fi
$OPENSSL list -kem-algorithms 2>/dev/null | grep -iE 'mlkem|kyber'   || echo 'no native ML-KEM (try "$OPENSSL list -providers" for oqsprovider)'

# 3) Live handshake against your server. The 'Negotiated TLS1.3 group' line
#    (or 'Server Temp Key' on OpenSSL 3.5) tells you what was negotiated
#    end-to-end — no external service required.
$OPENSSL s_client -connect example.com:443 -tls1_3 -groups X25519MLKEM768   </dev/null 2>&1 |
  grep -E 'Negotiated TLS1\.3 group|Server Temp Key|Cipher is|alert'

Expected when PQC is ON

OpenSSL 3.6.2 7 Apr 2026
  X25519MLKEM768 @ default
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

What you'll see when PQC is OFF

OpenSSL 3.0.13 30 Jan 2024
no native ML-KEM
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server Temp Key: X25519, 253 bits

To enable PQ on your server, see the nginx, Apache, or Caddy KB pages — it's one line: ssl_ecdh_curve / SSLOpenSSLConfCmd Groups / tls.curve_preferences = X25519MLKEM768:X25519:secp384r1.

Windows PowerShell

# 1) No-dependency check — identify this machine first.
[System.Environment]::OSVersion.Version
Get-ComputerInfo | Select-Object OsName,OsVersion,OsBuildNumber

# 2) Dependency check — prompt before installing anything.
function Confirm-Install($Message) {
  $answer = Read-Host "$Message Install now? [y/N]"
  return ($answer -match '^[Yy]')
}

function Get-OpenSSLPath {
  $cmd = Get-Command openssl.exe -ErrorAction SilentlyContinue
  if ($cmd) { return $cmd.Source }

  $programFilesX86 = [Environment]::GetEnvironmentVariable('ProgramFiles(x86)')
  $currentPath = (Get-Location).Path
  $candidateDirs = @(
    $(if ($env:ProgramFiles) { Join-Path (Join-Path $env:ProgramFiles 'OpenSSL-Win64') 'bin' }),
    $(if ($programFilesX86) { Join-Path (Join-Path $programFilesX86 'OpenSSL-Win32') 'bin' }),
    $(if ($env:LOCALAPPDATA) { Join-Path (Join-Path (Join-Path $env:LOCALAPPDATA 'Programs') 'OpenSSL-Win64') 'bin' }),
    $currentPath,
    $(if ($currentPath) { Join-Path (Join-Path $currentPath 'openssl') 'bin' })
  )
  foreach ($dir in $candidateDirs) {
    if (-not $dir) { continue }
    $candidate = Join-Path $dir 'openssl.exe'
    if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Leaf)) { return $candidate }
  }
  return $null
}

$openssl = Get-OpenSSLPath
if (-not $openssl) {
  Write-Warning 'OpenSSL 3.5+ was not found. Built-in Windows PowerShell/Schannel cannot prove the PQC named group.'
  if ((Get-Command winget -ErrorAction SilentlyContinue) -and (Confirm-Install 'Install ShiningLight OpenSSL with winget?')) {
    winget install ShiningLight.OpenSSL.Light
    $openssl = Get-OpenSSLPath
  }
  if (-not $openssl) { 'Install or pre-stage OpenSSL 3.5+ and retry.'; return }
}

& $openssl version
$groups = (& $openssl list -tls-groups 2>$null) -join [Environment]::NewLine
if ($groups -notmatch 'X25519MLKEM768|MLKEM|Kyber') {
  'Local OpenSSL does not advertise ML-KEM groups. Install or pre-stage OpenSSL 3.5+ and retry.'
  return
}
& $openssl list -kem-algorithms | Select-String -Pattern 'mlkem|kyber'

# 3) Live handshake — fully local, no api.checkpqc.app needed.
& $openssl s_client -connect example.com:443 -tls1_3 -groups X25519MLKEM768 2>&1 |
  Select-String -Pattern 'Negotiated TLS1\.3 group|Server Temp Key|Cipher is|alert'

Expected when PQC is ON

OpenSSL 3.6.2 7 Apr 2026
  X25519MLKEM768 @ default
Negotiated TLS1.3 group: X25519MLKEM768
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

What you'll see when PQC is OFF

OpenSSL 1.1.1w  11 Sep 2023
# (no mlkem line)
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
# (no group line — 1.1.1 cannot offer ML-KEM)

Schannel cannot expose the negotiated TLS named group programmatically, so a local OpenSSL binary is the most reliable offline answer on Windows.

HTTPS Load Balancer

Google enabled X25519Kyber768 on the External HTTPS Load Balancer in 2023 and migrated to the standardized X25519MLKEM768 in 2024. Capable clients negotiate hybrid PQC automatically — there is no toggle to flip.

Use a modern SSL policy:

gcloud compute ssl-policies create modern-tls13 \
  --profile MODERN \
  --min-tls-version 1.2

Attach it to the target HTTPS proxy.

Cloud CDN

Inherits the HTTPS LB TLS stack. Same hybrid PQC behavior.

App Engine, Cloud Run, Cloud Functions

Front-ended by Google Front Ends (GFE) which negotiate hybrid PQC with capable clients. No customer configuration.

Verify

if ! command -v openssl >/dev/null 2>&1; then
  echo 'OpenSSL was not found.'
  printf 'Install OpenSSL now? [y/N] '; read answer
  case "$answer" in [Yy]*) sudo apt-get update && sudo apt-get install -y openssl ;; *) exit 1 ;; esac
fi
openssl s_client -connect www.google.com:443 -tls1_3 \
  -groups X25519MLKEM768 </dev/null 2>&1 | grep "Cipher is\|alert"

Background

Google Cloud's HTTPS load balancer terminates TLS at the same edge that fronts google.com. Google deployed X25519Kyber768 at the edge in 2023 and transitioned to X25519MLKEM768 alongside Chrome's stable rollout. The Cloud HTTPS LB inherits this; no per-backend toggle exists.

Status by service

SSL policies

Hybrid groups are negotiated when both ends advertise them; GCP's SSL policy controls protocol versions and cipher suites, not named groups directly.

References

Run the check on your site →