From 1dce65308dd36872e573561f6d959103b790ec03 Mon Sep 17 00:00:00 2001 From: Folke Behrens Date: Thu, 12 Jun 2025 16:12:47 +0000 Subject: [PATCH] Update base64 to 0.22 (#12215) ## Problem Base64 0.13 is outdated. ## Summary of changes Update base64 to 0.22. Affects mostly proxy and proxy libs. Also upgrade serde_with to remove another dep on base64 0.13 from dep tree. --- Cargo.lock | 27 ++++++++----------- Cargo.toml | 4 +-- control_plane/src/endpoint.rs | 6 +++-- libs/proxy/postgres-protocol2/Cargo.toml | 2 +- .../src/authentication/sasl.rs | 15 ++++++++--- .../postgres-protocol2/src/password/mod.rs | 8 +++--- proxy/src/auth/backend/jwt.rs | 20 +++++++------- proxy/src/sasl/channel_binding.rs | 11 +++++--- proxy/src/scram/exchange.rs | 4 ++- proxy/src/scram/messages.rs | 13 +++++---- proxy/src/scram/mod.rs | 4 ++- proxy/src/scram/secret.rs | 8 +++--- proxy/src/serverless/local_conn_pool.rs | 6 +++-- proxy/src/tls/mod.rs | 4 ++- workspace_hack/Cargo.toml | 3 +-- 15 files changed, 76 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8bf04e87f..54a4f946d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,12 +903,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" - [[package]] name = "base64" version = "0.21.7" @@ -1300,7 +1294,7 @@ dependencies = [ "aws-smithy-types", "axum", "axum-extra", - "base64 0.13.1", + "base64 0.22.1", "bytes", "camino", "cfg-if", @@ -1426,7 +1420,7 @@ name = "control_plane" version = "0.1.0" dependencies = [ "anyhow", - "base64 0.13.1", + "base64 0.22.1", "camino", "clap", "comfy-table", @@ -4818,7 +4812,7 @@ dependencies = [ name = "postgres-protocol2" version = "0.1.0" dependencies = [ - "base64 0.20.0", + "base64 0.22.1", "byteorder", "bytes", "fallible-iterator", @@ -5190,7 +5184,7 @@ dependencies = [ "aws-config", "aws-sdk-iam", "aws-sigv4", - "base64 0.13.1", + "base64 0.22.1", "bstr", "bytes", "camino", @@ -6494,15 +6488,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.3" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", + "indexmap 2.9.0", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -6510,9 +6506,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.3.3" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", @@ -8583,7 +8579,6 @@ dependencies = [ "anyhow", "axum", "axum-core", - "base64 0.13.1", "base64 0.21.7", "base64ct", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 666ead7352..2f4fcbc249 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ aws-sigv4 = { version = "1.2", features = ["sign-http"] } aws-types = "1.3" axum = { version = "0.8.1", features = ["ws"] } axum-extra = { version = "0.10.0", features = ["typed-header", "query"] } -base64 = "0.13.0" +base64 = "0.22" bincode = "1.3" bindgen = "0.71" bit_field = "0.10.2" @@ -171,7 +171,7 @@ sentry = { version = "0.37", default-features = false, features = ["backtrace", serde = { version = "1.0", features = ["derive"] } serde_json = "1" serde_path_to_error = "0.1" -serde_with = { version = "2.0", features = [ "base64" ] } +serde_with = { version = "3", features = [ "base64" ] } serde_assert = "0.5.0" sha2 = "0.10.2" signal-hook = "0.3" diff --git a/control_plane/src/endpoint.rs b/control_plane/src/endpoint.rs index 774a0053f8..bc3a3a4e38 100644 --- a/control_plane/src/endpoint.rs +++ b/control_plane/src/endpoint.rs @@ -45,6 +45,8 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use anyhow::{Context, Result, anyhow, bail}; +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; use compute_api::requests::{ COMPUTE_AUDIENCE, ComputeClaims, ComputeClaimsScope, ConfigurationRequest, }; @@ -164,7 +166,7 @@ impl ComputeControlPlane { public_key_use: Some(PublicKeyUse::Signature), key_operations: Some(vec![KeyOperations::Verify]), key_algorithm: Some(KeyAlgorithm::EdDSA), - key_id: Some(base64::encode_config(key_hash, base64::URL_SAFE_NO_PAD)), + key_id: Some(BASE64_URL_SAFE_NO_PAD.encode(key_hash)), x509_url: None::, x509_chain: None::>, x509_sha1_fingerprint: None::, @@ -173,7 +175,7 @@ impl ComputeControlPlane { algorithm: AlgorithmParameters::OctetKeyPair(OctetKeyPairParameters { key_type: OctetKeyPairType::OctetKeyPair, curve: EllipticCurve::Ed25519, - x: base64::encode_config(public_key, base64::URL_SAFE_NO_PAD), + x: BASE64_URL_SAFE_NO_PAD.encode(public_key), }), }], }) diff --git a/libs/proxy/postgres-protocol2/Cargo.toml b/libs/proxy/postgres-protocol2/Cargo.toml index 7ebb05eec1..9c8f8f3531 100644 --- a/libs/proxy/postgres-protocol2/Cargo.toml +++ b/libs/proxy/postgres-protocol2/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" license = "MIT/Apache-2.0" [dependencies] -base64 = "0.20" +base64.workspace = true byteorder.workspace = true bytes.workspace = true fallible-iterator.workspace = true diff --git a/libs/proxy/postgres-protocol2/src/authentication/sasl.rs b/libs/proxy/postgres-protocol2/src/authentication/sasl.rs index 2daf9a80d4..a7bf3da20a 100644 --- a/libs/proxy/postgres-protocol2/src/authentication/sasl.rs +++ b/libs/proxy/postgres-protocol2/src/authentication/sasl.rs @@ -3,6 +3,8 @@ use std::fmt::Write; use std::{io, iter, mem, str}; +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; use hmac::{Hmac, Mac}; use rand::{self, Rng}; use sha2::digest::FixedOutput; @@ -226,7 +228,7 @@ impl ScramSha256 { let (client_key, server_key) = match password { Credentials::Password(password) => { - let salt = match base64::decode(parsed.salt) { + let salt = match BASE64_STANDARD.decode(parsed.salt) { Ok(salt) => salt, Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), }; @@ -255,7 +257,7 @@ impl ScramSha256 { let mut cbind_input = vec![]; cbind_input.extend(channel_binding.gs2_header().as_bytes()); cbind_input.extend(channel_binding.cbind_data()); - let cbind_input = base64::encode(&cbind_input); + let cbind_input = BASE64_STANDARD.encode(&cbind_input); self.message.clear(); write!(&mut self.message, "c={},r={}", cbind_input, parsed.nonce).unwrap(); @@ -272,7 +274,12 @@ impl ScramSha256 { *proof ^= signature; } - write!(&mut self.message, ",p={}", base64::encode(client_proof)).unwrap(); + write!( + &mut self.message, + ",p={}", + BASE64_STANDARD.encode(client_proof) + ) + .unwrap(); self.state = State::Finish { server_key, @@ -306,7 +313,7 @@ impl ScramSha256 { ServerFinalMessage::Verifier(verifier) => verifier, }; - let verifier = match base64::decode(verifier) { + let verifier = match BASE64_STANDARD.decode(verifier) { Ok(verifier) => verifier, Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), }; diff --git a/libs/proxy/postgres-protocol2/src/password/mod.rs b/libs/proxy/postgres-protocol2/src/password/mod.rs index 4cd9bfb060..e00ca1e34c 100644 --- a/libs/proxy/postgres-protocol2/src/password/mod.rs +++ b/libs/proxy/postgres-protocol2/src/password/mod.rs @@ -6,6 +6,8 @@ //! side. This is good because it ensures the cleartext password won't //! end up in logs pg_stat displays, etc. +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; use hmac::{Hmac, Mac}; use rand::RngCore; use sha2::digest::FixedOutput; @@ -83,8 +85,8 @@ pub(crate) async fn scram_sha_256_salt( format!( "SCRAM-SHA-256${}:{}${}:{}", SCRAM_DEFAULT_ITERATIONS, - base64::encode(salt), - base64::encode(stored_key), - base64::encode(server_key) + BASE64_STANDARD.encode(salt), + BASE64_STANDARD.encode(stored_key), + BASE64_STANDARD.encode(server_key) ) } diff --git a/proxy/src/auth/backend/jwt.rs b/proxy/src/auth/backend/jwt.rs index a48f67199a..5edc878243 100644 --- a/proxy/src/auth/backend/jwt.rs +++ b/proxy/src/auth/backend/jwt.rs @@ -4,6 +4,8 @@ use std::sync::Arc; use std::time::{Duration, SystemTime}; use arc_swap::ArcSwapOption; +use base64::Engine as _; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; use clashmap::ClashMap; use jose_jwk::crypto::KeyInfo; use reqwest::{Client, redirect}; @@ -347,17 +349,17 @@ impl JwkCacheEntryLock { .split_once('.') .ok_or(JwtEncodingError::InvalidCompactForm)?; - let header = base64::decode_config(header, base64::URL_SAFE_NO_PAD)?; + let header = BASE64_URL_SAFE_NO_PAD.decode(header)?; let header = serde_json::from_slice::>(&header)?; - let payloadb = base64::decode_config(payload, base64::URL_SAFE_NO_PAD)?; + let payloadb = BASE64_URL_SAFE_NO_PAD.decode(payload)?; let payload = serde_json::from_slice::>(&payloadb)?; if let Some(iss) = &payload.issuer { ctx.set_jwt_issuer(iss.as_ref().to_owned()); } - let sig = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?; + let sig = BASE64_URL_SAFE_NO_PAD.decode(signature)?; let kid = header.key_id.ok_or(JwtError::MissingKeyId)?; @@ -796,7 +798,6 @@ mod tests { use std::net::SocketAddr; use std::time::SystemTime; - use base64::URL_SAFE_NO_PAD; use bytes::Bytes; use http::Response; use http_body_util::Full; @@ -871,9 +872,8 @@ mod tests { key_id: Some(Cow::Owned(kid)), }; - let header = - base64::encode_config(serde_json::to_string(&header).unwrap(), URL_SAFE_NO_PAD); - let body = base64::encode_config(serde_json::to_string(&body).unwrap(), URL_SAFE_NO_PAD); + let header = BASE64_URL_SAFE_NO_PAD.encode(serde_json::to_string(&header).unwrap()); + let body = BASE64_URL_SAFE_NO_PAD.encode(serde_json::to_string(&body).unwrap()); format!("{header}.{body}") } @@ -883,7 +883,7 @@ mod tests { let payload = build_jwt_payload(kid, jose_jwa::Signing::Es256); let sig: Signature = SigningKey::from(key).sign(payload.as_bytes()); - let sig = base64::encode_config(sig.to_bytes(), URL_SAFE_NO_PAD); + let sig = BASE64_URL_SAFE_NO_PAD.encode(sig.to_bytes()); format!("{payload}.{sig}") } @@ -893,7 +893,7 @@ mod tests { let payload = build_custom_jwt_payload(kid, body, jose_jwa::Signing::Es256); let sig: Signature = SigningKey::from(key).sign(payload.as_bytes()); - let sig = base64::encode_config(sig.to_bytes(), URL_SAFE_NO_PAD); + let sig = BASE64_URL_SAFE_NO_PAD.encode(sig.to_bytes()); format!("{payload}.{sig}") } @@ -904,7 +904,7 @@ mod tests { let payload = build_jwt_payload(kid, jose_jwa::Signing::Rs256); let sig = SigningKey::::new(key).sign(payload.as_bytes()); - let sig = base64::encode_config(sig.to_bytes(), URL_SAFE_NO_PAD); + let sig = BASE64_URL_SAFE_NO_PAD.encode(sig.to_bytes()); format!("{payload}.{sig}") } diff --git a/proxy/src/sasl/channel_binding.rs b/proxy/src/sasl/channel_binding.rs index fdd011448e..e548cf3a83 100644 --- a/proxy/src/sasl/channel_binding.rs +++ b/proxy/src/sasl/channel_binding.rs @@ -1,5 +1,8 @@ //! Definition and parser for channel binding flag (a part of the `GS2` header). +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; + /// Channel binding flag (possibly with params). #[derive(Debug, PartialEq, Eq)] pub(crate) enum ChannelBinding { @@ -55,7 +58,7 @@ impl ChannelBinding { let mut cbind_input = vec![]; write!(&mut cbind_input, "p={mode},,",).unwrap(); cbind_input.extend_from_slice(get_cbind_data(mode)?); - base64::encode(&cbind_input).into() + BASE64_STANDARD.encode(&cbind_input).into() } }) } @@ -70,9 +73,9 @@ mod tests { use ChannelBinding::*; let cases = [ - (NotSupportedClient, base64::encode("n,,")), - (NotSupportedServer, base64::encode("y,,")), - (Required("foo"), base64::encode("p=foo,,bar")), + (NotSupportedClient, BASE64_STANDARD.encode("n,,")), + (NotSupportedServer, BASE64_STANDARD.encode("y,,")), + (Required("foo"), BASE64_STANDARD.encode("p=foo,,bar")), ]; for (cb, input) in cases { diff --git a/proxy/src/scram/exchange.rs b/proxy/src/scram/exchange.rs index abd5aeae5b..3ba8a79368 100644 --- a/proxy/src/scram/exchange.rs +++ b/proxy/src/scram/exchange.rs @@ -2,6 +2,8 @@ use std::convert::Infallible; +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; use hmac::{Hmac, Mac}; use sha2::Sha256; @@ -105,7 +107,7 @@ pub(crate) async fn exchange( secret: &ServerSecret, password: &[u8], ) -> sasl::Result> { - let salt = base64::decode(&secret.salt_base64)?; + let salt = BASE64_STANDARD.decode(&secret.salt_base64)?; let client_key = derive_client_key(pool, endpoint, password, &salt, secret.iterations).await; if secret.is_password_invalid(&client_key).into() { diff --git a/proxy/src/scram/messages.rs b/proxy/src/scram/messages.rs index e071417dab..42039f099c 100644 --- a/proxy/src/scram/messages.rs +++ b/proxy/src/scram/messages.rs @@ -3,6 +3,9 @@ use std::fmt; use std::ops::Range; +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; + use super::base64_decode_array; use super::key::{SCRAM_KEY_LEN, ScramKey}; use super::signature::SignatureBuilder; @@ -88,7 +91,7 @@ impl<'a> ClientFirstMessage<'a> { let mut message = String::new(); write!(&mut message, "r={}", self.nonce).unwrap(); - base64::encode_config_buf(nonce, base64::STANDARD, &mut message); + BASE64_STANDARD.encode_string(nonce, &mut message); let combined_nonce = 2..message.len(); write!(&mut message, ",s={salt_base64},i={iterations}").unwrap(); @@ -142,11 +145,7 @@ impl<'a> ClientFinalMessage<'a> { server_key: &ScramKey, ) -> String { let mut buf = String::from("v="); - base64::encode_config_buf( - signature_builder.build(server_key), - base64::STANDARD, - &mut buf, - ); + BASE64_STANDARD.encode_string(signature_builder.build(server_key), &mut buf); buf } @@ -251,7 +250,7 @@ mod tests { "iiYEfS3rOgn8S3rtpSdrOsHtPLWvIkdgmHxA0hf3JNOAG4dU" ); assert_eq!( - base64::encode(msg.proof), + BASE64_STANDARD.encode(msg.proof), "SRpfsIVS4Gk11w1LqQ4QvCUBZYQmqXNSDEcHqbQ3CHI=" ); } diff --git a/proxy/src/scram/mod.rs b/proxy/src/scram/mod.rs index 4f764c6087..5f627e062c 100644 --- a/proxy/src/scram/mod.rs +++ b/proxy/src/scram/mod.rs @@ -15,6 +15,8 @@ mod secret; mod signature; pub mod threadpool; +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; pub(crate) use exchange::{Exchange, exchange}; use hmac::{Hmac, Mac}; pub(crate) use key::ScramKey; @@ -32,7 +34,7 @@ pub(crate) const METHODS_WITHOUT_PLUS: &[&str] = &[SCRAM_SHA_256]; fn base64_decode_array(input: impl AsRef<[u8]>) -> Option<[u8; N]> { let mut bytes = [0u8; N]; - let size = base64::decode_config_slice(input, base64::STANDARD, &mut bytes).ok()?; + let size = BASE64_STANDARD.decode_slice(input, &mut bytes).ok()?; if size != N { return None; } diff --git a/proxy/src/scram/secret.rs b/proxy/src/scram/secret.rs index 8c6a08d432..f03617f34d 100644 --- a/proxy/src/scram/secret.rs +++ b/proxy/src/scram/secret.rs @@ -1,5 +1,7 @@ //! Tools for SCRAM server secret management. +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; use subtle::{Choice, ConstantTimeEq}; use super::base64_decode_array; @@ -56,7 +58,7 @@ impl ServerSecret { // iteration count 1 for our generated passwords going forward. // PG16 users can set iteration count=1 already today. iterations: 1, - salt_base64: base64::encode(nonce), + salt_base64: BASE64_STANDARD.encode(nonce), stored_key: ScramKey::default(), server_key: ScramKey::default(), doomed: true, @@ -88,7 +90,7 @@ mod tests { assert_eq!(parsed.iterations, iterations); assert_eq!(parsed.salt_base64, salt); - assert_eq!(base64::encode(parsed.stored_key), stored_key); - assert_eq!(base64::encode(parsed.server_key), server_key); + assert_eq!(BASE64_STANDARD.encode(parsed.stored_key), stored_key); + assert_eq!(BASE64_STANDARD.encode(parsed.server_key), server_key); } } diff --git a/proxy/src/serverless/local_conn_pool.rs b/proxy/src/serverless/local_conn_pool.rs index bb5637cd5f..c367615fb8 100644 --- a/proxy/src/serverless/local_conn_pool.rs +++ b/proxy/src/serverless/local_conn_pool.rs @@ -16,6 +16,8 @@ use std::sync::atomic::AtomicUsize; use std::task::{Poll, ready}; use std::time::Duration; +use base64::Engine as _; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; use ed25519_dalek::{Signature, Signer, SigningKey}; use futures::Future; use futures::future::poll_fn; @@ -346,7 +348,7 @@ fn sign_jwt(sk: &SigningKey, payload: &[u8]) -> String { jwt.push_str("eyJhbGciOiJFZERTQSJ9."); // encode the jwt payload in-place - base64::encode_config_buf(payload, base64::URL_SAFE_NO_PAD, &mut jwt); + BASE64_URL_SAFE_NO_PAD.encode_string(payload, &mut jwt); // create the signature from the encoded header || payload let sig: Signature = sk.sign(jwt.as_bytes()); @@ -354,7 +356,7 @@ fn sign_jwt(sk: &SigningKey, payload: &[u8]) -> String { jwt.push('.'); // encode the jwt signature in-place - base64::encode_config_buf(sig.to_bytes(), base64::URL_SAFE_NO_PAD, &mut jwt); + BASE64_URL_SAFE_NO_PAD.encode_string(sig.to_bytes(), &mut jwt); debug_assert_eq!( jwt.len(), diff --git a/proxy/src/tls/mod.rs b/proxy/src/tls/mod.rs index 7fe71abf48..f576214255 100644 --- a/proxy/src/tls/mod.rs +++ b/proxy/src/tls/mod.rs @@ -3,6 +3,8 @@ pub mod postgres_rustls; pub mod server_config; use anyhow::Context; +use base64::Engine as _; +use base64::prelude::BASE64_STANDARD; use rustls::pki_types::CertificateDer; use sha2::{Digest, Sha256}; use tracing::{error, info}; @@ -58,7 +60,7 @@ impl TlsServerEndPoint { let oid = certificate.signature_algorithm.oid; if SHA256_OIDS.contains(&oid) { let tls_server_end_point: [u8; 32] = Sha256::new().chain_update(cert).finalize().into(); - info!(%subject, tls_server_end_point = %base64::encode(tls_server_end_point), "determined channel binding"); + info!(%subject, tls_server_end_point = %BASE64_STANDARD.encode(tls_server_end_point), "determined channel binding"); Ok(Self::Sha256(tls_server_end_point)) } else { error!(%subject, "unknown channel binding"); diff --git a/workspace_hack/Cargo.toml b/workspace_hack/Cargo.toml index 2b07889871..b74df50f86 100644 --- a/workspace_hack/Cargo.toml +++ b/workspace_hack/Cargo.toml @@ -20,8 +20,7 @@ anstream = { version = "0.6" } anyhow = { version = "1", features = ["backtrace"] } axum = { version = "0.8", features = ["ws"] } axum-core = { version = "0.5", default-features = false, features = ["tracing"] } -base64-594e8ee84c453af0 = { package = "base64", version = "0.13", features = ["alloc"] } -base64-647d43efb71741da = { package = "base64", version = "0.21" } +base64 = { version = "0.21" } base64ct = { version = "1", default-features = false, features = ["std"] } bytes = { version = "1", features = ["serde"] } camino = { version = "1", default-features = false, features = ["serde1"] }