Skip to content

Commit 482c942

Browse files
authored
PostgreSQL SASL – run SHA256 in a blocking executor (#4006)
* Yield while salting password for PG SASL To prevent spenting too much time doing synchronous work on the event loop, we yield every few iterations of the hmac-sha256. * Remove unused bench
1 parent 69bb595 commit 482c942

File tree

1 file changed

+13
-22
lines changed
  • sqlx-postgres/src/connection

1 file changed

+13
-22
lines changed

sqlx-postgres/src/connection/sasl.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::connection::stream::PgStream;
22
use crate::error::Error;
33
use crate::message::{Authentication, AuthenticationSasl, SaslInitialResponse, SaslResponse};
4+
use crate::rt;
45
use crate::PgConnectOptions;
56
use hmac::{Hmac, Mac};
67
use rand::Rng;
@@ -90,7 +91,8 @@ pub(crate) async fn authenticate(
9091
options.password.as_deref().unwrap_or_default(),
9192
&cont.salt,
9293
cont.iterations,
93-
)?;
94+
)
95+
.await?;
9496

9597
// ClientKey := HMAC(SaltedPassword, "Client Key")
9698
let mut mac = Hmac::<Sha256>::new_from_slice(&salted_password).map_err(Error::protocol)?;
@@ -187,7 +189,7 @@ fn gen_nonce() -> String {
187189
}
188190

189191
// Hi(str, salt, i):
190-
fn hi<'a>(s: &'a str, salt: &'a [u8], iter_count: u32) -> Result<[u8; 32], Error> {
192+
async fn hi<'a>(s: &'a str, salt: &'a [u8], iter_count: u32) -> Result<[u8; 32], Error> {
191193
let mut mac = Hmac::<Sha256>::new_from_slice(s.as_bytes()).map_err(Error::protocol)?;
192194

193195
mac.update(salt);
@@ -196,30 +198,19 @@ fn hi<'a>(s: &'a str, salt: &'a [u8], iter_count: u32) -> Result<[u8; 32], Error
196198
let mut u = mac.finalize_reset().into_bytes();
197199
let mut hi = u;
198200

199-
for _ in 1..iter_count {
201+
for i in 1..iter_count {
200202
mac.update(u.as_slice());
201203
u = mac.finalize_reset().into_bytes();
202204
hi = hi.iter().zip(u.iter()).map(|(&a, &b)| a ^ b).collect();
205+
206+
// For large iteration counts, this process can take a long time and block the event loop.
207+
// It was measured as taking ~50ms for 4096 iterations (the default) on a developer machine.
208+
// If we want to yield every 10-100us (as generally advised for tokio), then we can yield
209+
// every 5 iterations which should be every ~50us.
210+
if i % 5 == 0 {
211+
rt::yield_now().await;
212+
}
203213
}
204214

205215
Ok(hi.into())
206216
}
207-
208-
#[cfg(all(test, not(debug_assertions)))]
209-
#[bench]
210-
fn bench_sasl_hi(b: &mut test::Bencher) {
211-
use test::black_box;
212-
213-
let mut rng = rand::thread_rng();
214-
let nonce: Vec<u8> = std::iter::repeat(())
215-
.map(|()| rng.sample(rand::distributions::Alphanumeric))
216-
.take(64)
217-
.collect();
218-
b.iter(|| {
219-
let _ = hi(
220-
test::black_box("secret_password"),
221-
test::black_box(&nonce),
222-
test::black_box(4096),
223-
);
224-
});
225-
}

0 commit comments

Comments
 (0)