From 824316ab31843afa8f29a79f009de11aff97adfc Mon Sep 17 00:00:00 2001 From: Sylvain Pierrot Date: Thu, 12 Mar 2026 16:31:53 +0000 Subject: [PATCH 1/4] feat(mysql): make rsa dep optional behind a rsa feature flag Signed-off-by: Sylvain Pierrot --- sqlx-mysql/Cargo.toml | 3 +- sqlx-mysql/src/connection/auth.rs | 61 +++++++++++++++++++------------ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/sqlx-mysql/Cargo.toml b/sqlx-mysql/Cargo.toml index c7afc236c6..eda4ff8a2f 100644 --- a/sqlx-mysql/Cargo.toml +++ b/sqlx-mysql/Cargo.toml @@ -14,6 +14,7 @@ json = ["sqlx-core/json", "serde"] any = ["sqlx-core/any"] offline = ["sqlx-core/offline", "serde/derive", "bitflags/serde"] migrate = ["sqlx-core/migrate"] +rsa = ["dep:rsa"] # Type Integration features bigdecimal = ["dep:bigdecimal", "sqlx-core/bigdecimal"] @@ -38,7 +39,7 @@ hkdf = "0.12.0" hmac = { version = "0.12.0", default-features = false } md-5 = { version = "0.10.0", default-features = false } rand = { version = "0.8.4", default-features = false, features = ["std", "std_rng"] } -rsa = "0.9" +rsa = { version = "0.9", optional = true } sha1 = { version = "0.10.1", default-features = false } sha2 = { version = "0.10.0", default-features = false } diff --git a/sqlx-mysql/src/connection/auth.rs b/sqlx-mysql/src/connection/auth.rs index 613f8e702f..f2d0cd56cf 100644 --- a/sqlx-mysql/src/connection/auth.rs +++ b/sqlx-mysql/src/connection/auth.rs @@ -2,7 +2,9 @@ use bytes::buf::Chain; use bytes::Bytes; use digest::{Digest, OutputSizeUser}; use generic_array::GenericArray; +#[cfg(feature = "rsa")] use rand::thread_rng; +#[cfg(feature = "rsa")] use rsa::{pkcs8::DecodePublicKey, Oaep, RsaPublicKey}; use sha1::Sha1; use sha2::Sha256; @@ -142,29 +144,41 @@ async fn encrypt_rsa<'s>( return Ok(to_asciz(password)); } - // client sends a public key request - stream.write_packet(&[public_key_request_id][..])?; - stream.flush().await?; - - // server sends a public key response - let packet = stream.recv_packet().await?; - let rsa_pub_key = &packet[1..]; - - // xor the password with the given nonce - let mut pass = to_asciz(password); - - let (a, b) = (nonce.first_ref(), nonce.last_ref()); - let mut nonce = Vec::with_capacity(a.len() + b.len()); - nonce.extend_from_slice(a); - nonce.extend_from_slice(b); - - xor_eq(&mut pass, &nonce); - - // client sends an RSA encrypted password - let pkey = parse_rsa_pub_key(rsa_pub_key)?; - let padding = Oaep::new::(); - pkey.encrypt(&mut thread_rng(), padding, &pass[..]) - .map_err(Error::protocol) + // Non-TLS RSA password encryption requires the `rsa` feature. + // It is opt-in because RUSTSEC-2023-0071 (Marvin Attack timing side-channel) + // has no patched release; disable it when all connections use TLS. + #[cfg(not(feature = "rsa"))] + return Err(err_protocol!( + "sha256_password / caching_sha2_password over non-TLS requires the `rsa` feature \ + (disabled by default due to RUSTSEC-2023-0071)" + )); + + #[cfg(feature = "rsa")] + { + // client sends a public key request + stream.write_packet(&[public_key_request_id][..])?; + stream.flush().await?; + + // server sends a public key response + let packet = stream.recv_packet().await?; + let rsa_pub_key = &packet[1..]; + + // xor the password with the given nonce + let mut pass = to_asciz(password); + + let (a, b) = (nonce.first_ref(), nonce.last_ref()); + let mut nonce = Vec::with_capacity(a.len() + b.len()); + nonce.extend_from_slice(a); + nonce.extend_from_slice(b); + + xor_eq(&mut pass, &nonce); + + // client sends an RSA encrypted password + let pkey = parse_rsa_pub_key(rsa_pub_key)?; + let padding = Oaep::new::(); + pkey.encrypt(&mut thread_rng(), padding, &pass[..]) + .map_err(Error::protocol) + } } // XOR(x, y) @@ -186,6 +200,7 @@ fn to_asciz(s: &str) -> Vec { } // https://docs.rs/rsa/0.3.0/rsa/struct.RSAPublicKey.html?search=#example-1 +#[cfg(feature = "rsa")] fn parse_rsa_pub_key(key: &[u8]) -> Result { let pem = std::str::from_utf8(key).map_err(Error::protocol)?; From 358124b7d472201af7ba62f72101c8e0a114c491 Mon Sep 17 00:00:00 2001 From: Sylvain Pierrot Date: Thu, 12 Mar 2026 16:44:39 +0000 Subject: [PATCH 2/4] fix: silence unused vars in mysql rsa auth Signed-off-by: Sylvain Pierrot --- sqlx-mysql/src/connection/auth.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sqlx-mysql/src/connection/auth.rs b/sqlx-mysql/src/connection/auth.rs index f2d0cd56cf..5353dea5f3 100644 --- a/sqlx-mysql/src/connection/auth.rs +++ b/sqlx-mysql/src/connection/auth.rs @@ -133,9 +133,9 @@ fn scramble_sha256( async fn encrypt_rsa<'s>( stream: &'s mut MySqlStream, - public_key_request_id: u8, + _public_key_request_id: u8, password: &'s str, - nonce: &'s Chain, + _nonce: &'s Chain, ) -> Result, Error> { // https://mariadb.com/kb/en/caching_sha2_password-authentication-plugin/ @@ -156,7 +156,7 @@ async fn encrypt_rsa<'s>( #[cfg(feature = "rsa")] { // client sends a public key request - stream.write_packet(&[public_key_request_id][..])?; + stream.write_packet(&[_public_key_request_id][..])?; stream.flush().await?; // server sends a public key response @@ -166,7 +166,7 @@ async fn encrypt_rsa<'s>( // xor the password with the given nonce let mut pass = to_asciz(password); - let (a, b) = (nonce.first_ref(), nonce.last_ref()); + let (a, b) = (_nonce.first_ref(), _nonce.last_ref()); let mut nonce = Vec::with_capacity(a.len() + b.len()); nonce.extend_from_slice(a); nonce.extend_from_slice(b); From 66ed091802495db47c66d3ec8ac1b22fe598f6ee Mon Sep 17 00:00:00 2001 From: Sylvain Pierrot Date: Thu, 12 Mar 2026 16:51:19 +0000 Subject: [PATCH 3/4] fix(mysql): make examples use mysql_native_password and silence rsa auth warnings Signed-off-by: Sylvain Pierrot --- .github/workflows/examples.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 5e90588619..9def25a143 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -50,6 +50,7 @@ jobs: services: mysql: image: mysql:latest + command: --default-authentication-plugin=mysql_native_password env: MYSQL_ROOT_PASSWORD: password ports: From d9e79c333209b1a0ce38193a682eca46ccbad2d0 Mon Sep 17 00:00:00 2001 From: Sylvain Pierrot Date: Thu, 12 Mar 2026 17:07:36 +0000 Subject: [PATCH 4/4] fix(mysql): avoid rsa requirement on non-TLS by using mysql_native_password in CI and tests Signed-off-by: Sylvain Pierrot --- tests/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index c2ccdabef6..609a2f2271 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -14,6 +14,7 @@ services: MYSQL_ROOT_HOST: '%' MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx + command: --default-authentication-plugin=mysql_native_password mysql_8_client_ssl: build: