[ 
https://issues.apache.org/jira/browse/PROTON-2934?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Andrew Stitcher reassigned PROTON-2934:
---------------------------------------

    Assignee: Clifford Jansen

> TLS 1.3 unsupported in Windows python-qpid-proton wheel
> -------------------------------------------------------
>
>                 Key: PROTON-2934
>                 URL: https://issues.apache.org/jira/browse/PROTON-2934
>             Project: Qpid Proton
>          Issue Type: Bug
>          Components: proton-c, python-binding
>         Environment: Windows 10 / 11 / Server 2019+, any Python 
> `cp3x-win_amd64` wheel of `python-qpid-proton`
>            Reporter: Clemens Vasters
>            Assignee: Clifford Jansen
>            Priority: Major
>              Labels: python-binding, schannel, tls, windows
>
> The pre-built Windows wheel of `python-qpid-proton` (and, by extension, every 
> `proton-c` build on Windows that uses the bundled SChannel TLS backend) 
> cannot negotiate **TLS 1.3**. It tops out at TLS 1.2. This makes it 
> impossible to connect from Windows to any AMQP 1.0 broker that enforces a 
> TLS-1.3 minimum, e.g. an Azure Service Bus namespace with `minimumTlsVersion 
> = 1.3`.
> `c/src/ssl/schannel.cpp` (the implementation selected at build time when 
> `os.name == 'nt'` in `python/ext_build.py`) initialises its SChannel 
> credentials with the **legacy** `SCHANNEL_CRED` structure:
> ```cpp
> // c/src/ssl/schannel.cpp ~ L237-256 (main branch, checked 2026-05-22)
> SCHANNEL_CRED descriptor;
> memset(&descriptor, 0, sizeof(descriptor));
> descriptor.dwVersion = SCHANNEL_CRED_VERSION;
> descriptor.dwFlags   = SCH_CRED_NO_DEFAULT_CREDS | 
> SCH_CRED_MANUAL_CRED_VALIDATION;
> ...
> *status = AcquireCredentialsHandle(NULL, UNISP_NAME, direction, NULL,
>                                    &descriptor, NULL, NULL, &tmp_handle, 
> &expiry);
> ```
> Microsoft's own SChannel documentation states that:
> 1. `SCHANNEL_CRED` is **deprecated**; "Applications should use the 
> `SCH_CREDENTIALS` structure instead."
>    
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-schannel_cred
> 2. TLS 1.3 was introduced in Windows 11 and Windows Server 2022, **but** the 
> protocol can only be negotiated when an application uses the new 
> `SCH_CREDENTIALS` structure together with `TLS_PARAMETERS`. Code that still 
> calls `AcquireCredentialsHandle()` with `SCHANNEL_CRED` will at most 
> negotiate TLS 1.2 regardless of what the underlying Windows version supports.
>    
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-sch_credentials
>    https://learn.microsoft.com/en-us/windows-server/security/tls/tls-schannel
> A grep across `c/src/ssl/schannel.cpp` on `main` (2026-05-22) confirms there 
> is currently **no** code path using `SCH_CREDENTIALS`, no `TLS_PARAMETERS` 
> block, and `grbitEnabledProtocols` is never set:
> ```
> SCH_CREDENTIALS         : 0 occurrences
> grbitEnabledProtocols   : 0 occurrences
> TLS_PARAMETERS          : 0 occurrences
> SCHANNEL_CRED           : 2 occurrences (declaration + version assignment)
> ```
> So the Windows wheel literally has no way to ask SChannel for TLS 1.3 today.
> ## Evidence on the released wheel
> Tested with `python-qpid-proton == 0.40.0`, wheel tag 
> `cp311-cp311-win_amd64`, on Windows 11 x64, Python 3.11.9.
> 1. Binary string scan of the installed `cproton_ffi.pyd` (size 463 872 bytes):
>    | Pattern   | Matches |
>    |-----------|---------|
>    | `TLSv1.0` | 0 |
>    | `TLSv1.1` | 1 |
>    | `TLSv1.2` | 1 |
>    | `TLSv1.3` | **0** |
>    | `OpenSSL ` | 0 |
>    (No OpenSSL banner strings present, consistent with the wheel using 
> SChannel rather than OpenSSL.)
> 2. `proton.SSLDomain` exposes no API to pin the minimum/maximum TLS protocol 
> version. Public methods on `SSLDomain` are limited to: `set_credentials`, 
> `set_peer_authentication`, `set_trusted_ca_db`, `allow_unsecured_client`. 
> There is no equivalent of OpenSSL's `SSL_CTX_set_min_proto_version` / 
> `SSL_CTX_set_max_proto_version` reachable from the Python or C API.
> 3. Live reproduction against an Azure Service Bus namespace pinned to 
> `minimumTlsVersion = 1.3`: the connection fails at the AMQP open handshake 
> with:
>    ```
>    amqp:unauthorized-access "Invalid TLS version"
>    ```
>    The TCP/TLS handshake itself completes at TLS 1.2; the broker then closes 
> the AMQP session because the negotiated TLS version is below the namespace 
> policy. Repeating the same test from Linux (where the wheel uses OpenSSL) 
> succeeds.
> ## Minimal reproducer
> Requires:
> - Windows 10 1809 or newer (TLS 1.3 SChannel needs Win 11 / Server 2022 for 
> full client negotiation)
> - An Azure Service Bus namespace with `minimumTlsVersion = 1.3`. Equivalent: 
> any AMQP 1.0 broker behind a TLS terminator (e.g. nginx / haproxy / Envoy) 
> configured to require TLS 1.3.
> ```python
> # pip install python-qpid-proton==0.40.0 azure-identity
> import os
> from proton import Message
> from proton.utils import BlockingConnection
> from proton.reactor import Container
> # Replace with your namespace
> HOST = "<your-ns>.servicebus.windows.net"
> URL  = f"amqps://\{HOST}:5671"
> conn = BlockingConnection(URL, sasl_enabled=True, allowed_mechs="ANONYMOUS",
>                           ssl_domain=None)  # will fail before SASL on Win
> ```
> Expected (against a TLS-1.3-required broker): `proton.ConnectionException: 
> amqp:unauthorized-access`. On Linux against the same broker the connection 
> succeeds.
> ## Impact
> - All Python users on Windows are locked out of any AMQP 1.0 broker that 
> requires TLS 1.3.
> - TLS 1.3 is increasingly a baseline corporate / regulatory requirement 
> (PCI-DSS, FedRAMP High, several CSP "secure defaults" policies). For example, 
> Azure Service Bus customers under the `CloudGov_ServBus_TLS` policy cannot 
> lower `minimumTlsVersion` below 1.3.
> - The same code path is used by every C-binding language that links proton-c 
> on Windows, so the bug surface is wider than just the Python wheel.
> - Workarounds today require either (a) switching to a non-proton AMQP stack 
> on Windows, (b) running the Windows code under WSL/Linux, or (c) lowering the 
> broker's TLS minimum — all of which are unacceptable for many users.
> ## Suggested fix
> Switch the SChannel credential acquisition path in `c/src/ssl/schannel.cpp` 
> to use the modern `SCH_CREDENTIALS` structure, with a fallback to 
> `SCHANNEL_CRED` only when running on a Windows release that pre-dates 
> `SCH_CREDENTIALS` support (Windows 10 1809 / Server 2019).
> Outline (in `win_credential_cred_handle`):
> ```cpp
> #ifdef SCH_CREDENTIALS_VERSION   // present in modern Windows 10 SDKs
>     TLS_PARAMETERS tls_params = \{0};
>     // leave grbitDisabledProtocols = 0 → let SChannel pick the strongest 
> mutually-supported
>     // version, including TLS 1.3 when the peer offers it.
>     SCH_CREDENTIALS sch_creds = \{0};
>     sch_creds.dwVersion        = SCH_CREDENTIALS_VERSION;
>     sch_creds.dwFlags          = SCH_CRED_NO_DEFAULT_CREDS | 
> SCH_CRED_MANUAL_CRED_VALIDATION;
>     sch_creds.cTlsParameters   = 1;
>     sch_creds.pTlsParameters   = &tls_params;
>     if (cred->cert_context) {
>         sch_creds.paCred = &cred->cert_context;
>         sch_creds.cCreds = 1;
>     }
>     *status = AcquireCredentialsHandle(
>         NULL, UNISP_NAME, direction, NULL,
>         &sch_creds, NULL, NULL, &tmp_handle, &expiry);
>     if (*status == SEC_E_UNSUPPORTED_FUNCTION) {
>         // fallback path: old SCHANNEL_CRED for pre-1809 Windows
>         ... existing legacy code ...
>     }
> #else
>     ... existing legacy code ...
> #endif
> ```
> ## References
> - Microsoft Learn — `SCHANNEL_CRED` (deprecated): 
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-schannel_cred
> - Microsoft Learn — `SCH_CREDENTIALS` (replacement, supports TLS 1.3): 
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-sch_credentials
> - Microsoft Learn — `TLS_PARAMETERS`: 
> https://learn.microsoft.com/en-us/windows/win32/api/schannel/ns-schannel-tls_parameters
> - Microsoft Learn — TLS protocol support in SChannel (TLS 1.3 added in Win 11 
> / Server 2022): 
> https://learn.microsoft.com/en-us/windows-server/security/tls/tls-schannel
> - Related (already resolved) PROTON-1989 "TLS Configuration does not support 
> TLSv1_3 in OpenSSL v1.1.1" — fixed the OpenSSL path only.
> - Related PROTON-2397 "Update default client TLS defaults for verifying 
> outbound connections to AMQP servers" — also OpenSSL-only.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to