Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0cf37b3
Add a test for ssl with ssl.MemoryBIO
im-0 May 24, 2026
d625289
Add Serde serializer that converts structs into basic Python objects
im-0 May 26, 2026
ce22219
Support module monkey-patching on VM level
im-0 May 30, 2026
cac2ee7
Add OpenSSL data files used to build OID and NID tables
im-0 May 27, 2026
a54bbf0
Enable/disable ssl-related tests from cpython test suite
im-0 May 30, 2026
1e25e1a
Revert "Skip flaky tests (#7961)"
im-0 May 31, 2026
2cda4d8
Revert "Skip flaky test (#7967)"
im-0 May 31, 2026
0cb9242
Almost a complete rewrite of rustls integration
im-0 May 24, 2026
60cf7e1
Skip test.test_httplib.HTTPSTest.test_networked_good_cert if rustls i…
im-0 Jun 1, 2026
d06b7f5
On client, both CERT_OPTIONAL and CERT_REQUIRED enable cert verification
im-0 Jun 2, 2026
3acc916
Rename stdlib_ssl_bio_unencrypted_trailer.py -> stdlib_ssl.py
im-0 Jun 3, 2026
c5be311
Move crates/stdlib/src/rustls-data -> crates/stdlib/rustls-data
im-0 Jun 3, 2026
489cd9c
Use expectedFailureIf() instead of skipIf() for rustls-specific skips
im-0 Jun 3, 2026
f2f3f2a
Fix `cargo shear` warn (graviola dep for examples/custom_tls_provider…
im-0 Jun 3, 2026
cdd4433
Call SNI callback earlier and re-read the context after it
im-0 Jun 3, 2026
9bb9e45
Remove allow_threads() from rustls integration
im-0 Jun 3, 2026
bc9d497
Use certs from both SSL_CERT_FILE and SSL_CERT_DIR env vars if present
im-0 Jun 3, 2026
76c92ae
Keep rustls error in SslError until converted into Python error
im-0 Jun 3, 2026
e3c9596
Ensure that TLS alerts are sent back on TLS errors
im-0 Jun 3, 2026
bb0d7ef
Document TLS connection state machine
im-0 Jun 4, 2026
02eb49f
Test Rust -> Python serializer with py_serde::PyObjectSerializer
im-0 Jun 4, 2026
a25d257
Add documentation for Rust -> Python serializer
im-0 Jun 4, 2026
29013e3
Serialize unit struct into Python string with just a struct name
im-0 Jun 4, 2026
596b0da
Enable ThreadedTests.test_ssl_in_multiple_threads
im-0 Jun 4, 2026
f7117cf
Workaround for test.test_ssl.ThreadedTests.test_wrong_cert_tls*
im-0 Jun 1, 2026
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
Prev Previous commit
Next Next commit
Keep rustls error in SslError until converted into Python error
  • Loading branch information
im-0 committed Jun 4, 2026
commit 76c92aeb9b596029e0c70255f05e255725c1e880
8 changes: 4 additions & 4 deletions crates/stdlib/src/rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ mod _ssl {
};

Ok(ClientConnection::new(Arc::new(config), server_name)
.map_err(|e| SslError::from_rustls(e).into_py_err(vm))?
.map_err(|e| SslError::Rustls(e).into_py_err(vm))?
.into())
}

Expand All @@ -1214,7 +1214,7 @@ mod _ssl {
// TODO: Search by a requested host name too.
let cert_chain = self.cert_chain.read();
if cert_chain.is_empty() {
return Err(SslError::from_rustls(rustls::Error::PeerIncompatible(
return Err(SslError::Rustls(rustls::Error::PeerIncompatible(
rustls::PeerIncompatible::NoCipherSuitesInCommon,
))
.into_py_err(vm));
Expand Down Expand Up @@ -2707,7 +2707,7 @@ impl State {
Err(SslError::Ssl("TLS alert is too long or too short".to_string()).into_py_err(vm))
} else {
Ok(Self::ServerSendingAlert {
error: SslError::from_rustls(error).into_py_err(vm),
error: SslError::Rustls(error).into_py_err(vm),
alert_buf,
alert_buf_pos: 0,
})
Expand Down Expand Up @@ -2847,7 +2847,7 @@ impl Io {
let err = err
.downcast::<rustls::Error>()
.expect("BUG: Not a rustls Error");
Err(SslError::from_rustls(err).into_py_err(vm))
Err(SslError::Rustls(err).into_py_err(vm))
}

_ => Err(SslError::Io(err).into_py_err(vm)),
Expand Down
123 changes: 53 additions & 70 deletions crates/stdlib/src/ssl/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,19 +177,15 @@ pub(super) enum SslError {
ZeroReturn,
/// Unexpected EOF without close_notify (protocol violation)
Eof,
/// Certificate verification error
CertVerification(rustls::CertificateError),
/// rustls error
Rustls(rustls::Error),
/// I/O error
Io(std::io::Error),
/// Timeout error (socket.timeout)
#[expect(dead_code, reason = "TODO: Implement timeouts")]
Timeout(String),
/// Python exception (pass through directly)
Py(PyBaseExceptionRef),
/// TLS alert received with OpenSSL-compatible error code
AlertReceived { lib: i32, reason: i32 },
/// OpenSSL-compatible SSL reason code
OpenSslReason(i32),
}

impl SslError {
Expand All @@ -200,60 +196,6 @@ impl SslError {
1000 + (u8::from(alert) as i32)
}

/// Convert rustls error to SslError
pub(super) fn from_rustls(err: rustls::Error) -> Self {
match err {
rustls::Error::InvalidCertificate(cert_err) => Self::CertVerification(cert_err),
rustls::Error::AlertReceived(alert_desc) => {
// Map TLS alerts to OpenSSL-compatible error codes
// lib = 20 (ERR_LIB_SSL), reason = 1000 + alert_code
match alert_desc {
rustls::AlertDescription::CloseNotify => {
// Special case: close_notify is handled as ZeroReturn
Self::ZeroReturn
}
_ => {
// All other alerts: convert to OpenSSL error code
// This includes InternalError (80 -> reason 1080)
Self::AlertReceived {
lib: ERR_LIB_SSL,
reason: Self::alert_to_openssl_reason(alert_desc),
}
}
}
}
rustls::Error::PeerIncompatible(peer_err) => {
use rustls::PeerIncompatible;
let reason = match peer_err {
PeerIncompatible::NoCipherSuitesInCommon => SSL_R_NO_SHARED_CIPHER,
PeerIncompatible::NoKxGroupsInCommon
| PeerIncompatible::NoEcPointFormatsInCommon
| PeerIncompatible::EcPointsExtensionRequired
| PeerIncompatible::NamedGroupsExtensionRequired
| PeerIncompatible::UncompressedEcPointsRequired => SSL_R_NO_SUITABLE_GROUPS,
PeerIncompatible::KeyShareExtensionRequired => SSL_R_NO_SUITABLE_KEY_SHARE,
PeerIncompatible::NoCertificateRequestSignatureSchemesInCommon
| PeerIncompatible::NoSignatureSchemesInCommon
| PeerIncompatible::SignatureAlgorithmsExtensionRequired => {
SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM
}
PeerIncompatible::ServerDoesNotSupportTls12Or13
| PeerIncompatible::ServerTlsVersionIsDisabledByOurConfig
| PeerIncompatible::SupportedVersionsExtensionRequired
| PeerIncompatible::Tls12NotOffered
| PeerIncompatible::Tls12NotOfferedOrEnabled
| PeerIncompatible::Tls13RequiredForQuic => SSL_R_UNSUPPORTED_PROTOCOL,
_ => return Self::Ssl(format!("peer is incompatible: {peer_err:?}")),
};
Self::OpenSslReason(reason)
}
rustls::Error::NoApplicationProtocol => {
Self::OpenSslReason(SSL_R_NO_APPLICATION_PROTOCOL)
}
_ => Self::Ssl(format!("{err}")),
}
}

/// Create SSLError with library and reason from string values
///
/// This is the base helper for creating SSLError with _library and _reason
Expand Down Expand Up @@ -366,6 +308,57 @@ impl SslError {
Self::WantWrite => create_ssl_want_write_error(vm).upcast(),
Self::Timeout(msg) => timeout_error_msg(vm, msg).upcast(),
Self::Ssl(msg) => Self::create_plain_ssl_error(vm, msg),
Self::Rustls(err) => match err {
rustls::Error::InvalidCertificate(cert_err) => {
create_ssl_cert_verification_error(vm, &cert_err).expect("unlikely to happen")
}
rustls::Error::AlertReceived(rustls::AlertDescription::CloseNotify) => {
create_ssl_zero_return_error(vm).upcast()
}
rustls::Error::AlertReceived(alert_desc) => Self::create_ssl_error_from_codes(
vm,
ERR_LIB_SSL,
Self::alert_to_openssl_reason(alert_desc),
),
rustls::Error::PeerIncompatible(peer_err) => {
use rustls::PeerIncompatible;
let reason = match peer_err {
PeerIncompatible::NoCipherSuitesInCommon => SSL_R_NO_SHARED_CIPHER,
PeerIncompatible::NoKxGroupsInCommon
| PeerIncompatible::NoEcPointFormatsInCommon
| PeerIncompatible::EcPointsExtensionRequired
| PeerIncompatible::NamedGroupsExtensionRequired
| PeerIncompatible::UncompressedEcPointsRequired => {
SSL_R_NO_SUITABLE_GROUPS
}
PeerIncompatible::KeyShareExtensionRequired => SSL_R_NO_SUITABLE_KEY_SHARE,
PeerIncompatible::NoCertificateRequestSignatureSchemesInCommon
| PeerIncompatible::NoSignatureSchemesInCommon
| PeerIncompatible::SignatureAlgorithmsExtensionRequired => {
SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM
}
PeerIncompatible::ServerDoesNotSupportTls12Or13
| PeerIncompatible::ServerTlsVersionIsDisabledByOurConfig
| PeerIncompatible::SupportedVersionsExtensionRequired
| PeerIncompatible::Tls12NotOffered
| PeerIncompatible::Tls12NotOfferedOrEnabled
| PeerIncompatible::Tls13RequiredForQuic => SSL_R_UNSUPPORTED_PROTOCOL,
_ => {
return Self::create_plain_ssl_error(
vm,
format!("peer is incompatible: {peer_err:?}"),
);
}
};
Self::create_ssl_error_from_codes(vm, ERR_LIB_SSL, reason)
}
rustls::Error::NoApplicationProtocol => Self::create_ssl_error_from_codes(
vm,
ERR_LIB_SSL,
SSL_R_NO_APPLICATION_PROTOCOL,
),
_ => Self::create_plain_ssl_error(vm, err.to_string()),
},
Self::PemLib(msg) => Self::create_pem_ssl_error(vm, format!("PEM lib: {msg}"))
.expect("unlikely to happen"),
Self::FailedToReadDer(msg) => {
Expand All @@ -381,10 +374,6 @@ impl SslError {
),
Self::ZeroReturn => create_ssl_zero_return_error(vm).upcast(),
Self::Eof => create_ssl_eof_error(vm).upcast(),
Self::CertVerification(cert_err) => {
// Use the proper cert verification error creator
create_ssl_cert_verification_error(vm, &cert_err).expect("unlikely to happen")
}
Self::Io(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
create_ssl_eof_error(vm).upcast()
}
Expand All @@ -397,12 +386,6 @@ impl SslError {
.upcast(),
Self::Io(err) => err.into_pyexception(vm),
Self::Py(exc) => exc,
Self::AlertReceived { lib, reason } => {
Self::create_ssl_error_from_codes(vm, lib, reason)
}
Self::OpenSslReason(reason) => {
Self::create_ssl_error_from_codes(vm, ERR_LIB_SSL, reason)
}
}
}
}
Expand Down