diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd5e18e7..88b1b8c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,12 +80,14 @@ jobs: - name: Check workspace with native-tls run: > cargo hack check -p librespot --each-feature --exclude-all-features - --include-features native-tls --exclude-features rustls-tls + --include-features native-tls + --exclude-features rustls-tls-native-roots,rustls-tls-webpki-roots - - name: Check workspace with rustls-tls + - name: Check workspace with rustls-tls-native-roots run: > cargo hack check -p librespot --each-feature --exclude-all-features - --include-features rustls-tls --exclude-features native-tls + --include-features rustls-tls-native-roots + --exclude-features native-tls,rustls-tls-webpki-roots - name: Build binary with default features run: cargo build --frozen diff --git a/.github/workflows/cross-compile.yml b/.github/workflows/cross-compile.yml index f5befd82..79f7f586 100644 --- a/.github/workflows/cross-compile.yml +++ b/.github/workflows/cross-compile.yml @@ -61,14 +61,14 @@ jobs: toolchain: ${{ matrix.toolchain }} args: --locked --verbose - - name: Build binary with rustls-tls and no default features + - name: Build binary without system dependencies if: matrix.platform.target == 'riscv64gc-unknown-linux-gnu' uses: houseabsolute/actions-rust-cross@v1 with: command: build target: ${{ matrix.platform.target }} toolchain: ${{ matrix.toolchain }} - args: --locked --verbose --no-default-features --features rustls-tls + args: --locked --verbose --no-default-features --features rustls-tls-webpki-roots - name: Upload debug artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 7b42d163..fffa8a56 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -71,9 +71,17 @@ jobs: - name: Run clippy with native-tls run: > cargo hack clippy -p librespot --each-feature --exclude-all-features - --include-features native-tls --exclude-features rustls-tls + --include-features native-tls + --exclude-features rustls-tls-native-roots,rustls-tls-webpki-roots - - name: Run clippy with rustls-tls + - name: Run clippy with rustls-tls-native-roots run: > cargo hack clippy -p librespot --each-feature --exclude-all-features - --include-features rustls-tls --exclude-features native-tls + --include-features rustls-tls-native-roots + --exclude-features native-tls,rustls-tls-webpki-roots + + - name: Run clippy with rustls-tls-webpki-roots + run: > + cargo hack clippy -p librespot --each-feature --exclude-all-features + --include-features rustls-tls-webpki-roots + --exclude-features native-tls,rustls-tls-native-roots diff --git a/COMPILING.md b/COMPILING.md index 8eedca49..ee698f62 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -96,10 +96,18 @@ sudo dnf install openssl-devel pkg-config ``` #### rustls-tls -Uses a Rust-based TLS implementation with `rustls-platform-verifier` for certificate authority (CA) verification: +Uses a Rust-based TLS implementation with certificate authority (CA) verification. Two certificate store options are available: + +**rustls-tls-native-roots**: - **Linux**: Uses system ca-certificates package - **macOS**: Uses Security.framework for CA verification - **Windows**: Uses Windows certificate store +- Integrates with system certificate management and security updates + +**rustls-tls-webpki-roots**: +- Uses Mozilla's compiled-in certificate store (webpki-roots) +- Certificate trust is independent of host system +- Best for reproducible builds, containers, or embedded systems **When to choose rustls-tls:** - You want to avoid external OpenSSL dependencies @@ -118,8 +126,11 @@ cargo build # Explicitly use native-tls cargo build --no-default-features --features "native-tls rodio-backend with-libmdns" -# Use rustls-tls instead -cargo build --no-default-features --features "rustls-tls rodio-backend with-libmdns" +# Use rustls-tls with native certificate stores +cargo build --no-default-features --features "rustls-tls-native-roots rodio-backend with-libmdns" + +# Use rustls-tls with Mozilla's webpki certificate store +cargo build --no-default-features --features "rustls-tls-webpki-roots rodio-backend with-libmdns" ``` **Important:** The TLS backends are mutually exclusive. Attempting to enable both will result in a compile-time error. diff --git a/Cargo.lock b/Cargo.lock index 1408822f..d616e220 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1324,6 +1324,7 @@ dependencies = [ "tokio-rustls 0.25.0", "tower-service", "webpki", + "webpki-roots 0.26.11", ] [[package]] @@ -1343,6 +1344,7 @@ dependencies = [ "tokio", "tokio-rustls 0.25.0", "tower-service", + "webpki-roots 0.26.11", ] [[package]] @@ -1355,12 +1357,12 @@ dependencies = [ "hyper", "hyper-util", "rustls 0.23.31", + "rustls-native-certs 0.8.1", "rustls-pki-types", - "rustls-platform-verifier", "tokio", "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -3028,6 +3030,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.31", + "rustls-native-certs 0.8.1", "rustls-pki-types", "serde", "serde_json", @@ -3043,7 +3046,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -3201,33 +3204,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls 0.23.31", - "rustls-native-certs 0.8.1", - "rustls-platform-verifier-android", - "rustls-webpki 0.103.4", - "security-framework 3.3.0", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -3936,11 +3912,13 @@ dependencies = [ "log", "native-tls", "rustls 0.23.31", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", "tokio-native-tls", "tokio-rustls 0.26.2", "tungstenite", + "webpki-roots 0.26.11", ] [[package]] @@ -4366,12 +4344,12 @@ dependencies = [ ] [[package]] -name = "webpki-root-certs" -version = "1.0.2" +name = "webpki-roots" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "rustls-pki-types", + "webpki-roots 1.0.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 14b72db4..6810e256 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,14 +48,27 @@ default = ["native-tls", "rodio-backend", "with-libmdns"] # system-managed certificates. native-tls = ["librespot-core/native-tls", "librespot-oauth/native-tls"] -# rustls-tls: Uses the Rust-based rustls TLS implementation with platform certificate verification. -# This provides a Rust TLS stack (with assembly optimizations) that uses rustls-platform-verifier to -# automatically select the appropriate certificate authority (CA) certificates from your system's -# trust store. Choose this for avoiding external OpenSSL dependencies, reproducible builds, or when -# targeting platforms where native TLS dependencies are unavailable or problematic (musl, embedded, -# static linking). On Linux it uses ca-certificates, on macOS it uses Security.framework, and on -# Windows it uses the Windows certificate store. -rustls-tls = ["librespot-core/rustls-tls", "librespot-oauth/rustls-tls"] +# rustls-tls: Uses the Rust-based rustls TLS implementation with certificate authority (CA) +# verification. This provides a Rust TLS stack (with assembly optimizations). Choose this for +# avoiding external OpenSSL dependencies, reproducible builds, or when targeting platforms where +# native TLS dependencies are unavailable or problematic (musl, embedded, static linking). +# +# Two certificate store options are available: +# +# - rustls-tls-native-roots: Uses rustls with native system certificate stores (ca-certificates on +# Linux, Security.framework on macOS, Windows certificate store on Windows). Best for most users as +# it integrates with system-managed certificates and gets security updates through the OS. +rustls-tls-native-roots = [ + "librespot-core/rustls-tls-native-roots", + "librespot-oauth/rustls-tls-native-roots", +] +# rustls-tls-webpki-roots: Uses rustls with Mozilla's compiled-in certificate store (webpki-roots). +# Best for reproducible builds, containerized environments, or when you want certificate handling +# to be independent of the host system. +rustls-tls-webpki-roots = [ + "librespot-core/rustls-tls-webpki-roots", + "librespot-oauth/rustls-tls-webpki-roots", +] # Audio backends - see README.md for audio backend selection guide # Cross-platform backends: diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 217cdfcb..9d56ef2d 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -14,7 +14,8 @@ default = ["native-tls"] # TLS backend propagation native-tls = ["librespot-core/native-tls"] -rustls-tls = ["librespot-core/rustls-tls"] +rustls-tls-native-roots = ["librespot-core/rustls-tls-native-roots"] +rustls-tls-webpki-roots = ["librespot-core/rustls-tls-webpki-roots"] [dependencies] librespot-core.workspace = true diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 3da9195b..b9010d73 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -13,8 +13,9 @@ edition.workspace = true default = ["native-tls"] # TLS backend propagation -native-tls = ["librespot-core/native-tls", "librespot-playback/native-tls"] -rustls-tls = ["librespot-core/rustls-tls", "librespot-playback/rustls-tls"] +native-tls = ["librespot-core/native-tls"] +rustls-tls-native-roots = ["librespot-core/rustls-tls-native-roots"] +rustls-tls-webpki-roots = ["librespot-core/rustls-tls-webpki-roots"] [dependencies] librespot-core.workspace = true diff --git a/core/Cargo.toml b/core/Cargo.toml index 161ac5e9..729c328b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,12 +21,23 @@ native-tls = [ "librespot-oauth/native-tls", "tokio-tungstenite/native-tls", ] -rustls-tls = [ - "dep:hyper-rustls", +rustls-tls-native-roots = [ + "__rustls", "hyper-proxy2/rustls", - "librespot-oauth/rustls-tls", - "tokio-tungstenite/__rustls-tls", + "hyper-rustls/native-tokio", + "librespot-oauth/rustls-tls-native-roots", + "tokio-tungstenite/rustls-tls-native-roots", ] +rustls-tls-webpki-roots = [ + "__rustls", + "hyper-proxy2/rustls-webpki", + "hyper-rustls/webpki-tokio", + "librespot-oauth/rustls-tls-webpki-roots", + "tokio-tungstenite/rustls-tls-webpki-roots", +] + +# Internal features - these are not meant to be used by end users +__rustls = [] [dependencies] librespot-oauth.workspace = true @@ -60,8 +71,6 @@ hyper-rustls = { version = "0.27", default-features = false, features = [ "http1", "http2", "ring", - "rustls-platform-verifier", - "tls12", ], optional = true } hyper-tls = { version = "0.6", optional = true } hyper-util = { version = "0.1", default-features = false, features = [ diff --git a/core/src/http_client.rs b/core/src/http_client.rs index 27d0da92..9d5d54fc 100644 --- a/core/src/http_client.rs +++ b/core/src/http_client.rs @@ -22,9 +22,9 @@ use parking_lot::Mutex; use thiserror::Error; use url::Url; -#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))] +#[cfg(all(feature = "__rustls", not(feature = "native-tls")))] use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; -#[cfg(all(feature = "native-tls", not(feature = "rustls-tls")))] +#[cfg(all(feature = "native-tls", not(feature = "__rustls")))] use hyper_tls::HttpsConnector; use crate::{ @@ -150,13 +150,16 @@ impl HttpClient { fn try_create_hyper_client(proxy_url: Option<&Url>) -> Result { // configuring TLS is expensive and should be done once per process - #[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))] + #[cfg(all(feature = "__rustls", not(feature = "native-tls")))] let https_connector = { - let tls = HttpsConnectorBuilder::new().with_platform_verifier(); + #[cfg(feature = "rustls-tls-native-roots")] + let tls = HttpsConnectorBuilder::new().with_native_roots()?; + #[cfg(feature = "rustls-tls-webpki-roots")] + let tls = HttpsConnectorBuilder::new().with_webpki_roots(); tls.https_or_http().enable_http1().enable_http2().build() }; - #[cfg(all(feature = "native-tls", not(feature = "rustls-tls")))] + #[cfg(all(feature = "native-tls", not(feature = "__rustls")))] let https_connector = HttpsConnector::new(); // When not using a proxy a dummy proxy is configured that will not intercept any traffic. diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index 59b1eb32..6dfa6889 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -19,7 +19,8 @@ with-libmdns = ["dep:libmdns"] # TLS backend propagation native-tls = ["librespot-core/native-tls"] -rustls-tls = ["librespot-core/rustls-tls"] +rustls-tls-native-roots = ["librespot-core/rustls-tls-native-roots"] +rustls-tls-webpki-roots = ["librespot-core/rustls-tls-webpki-roots"] [dependencies] librespot-core.workspace = true diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 27d94724..b6b2321d 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -14,7 +14,8 @@ default = ["native-tls"] # TLS backend propagation native-tls = ["librespot-core/native-tls"] -rustls-tls = ["librespot-core/rustls-tls"] +rustls-tls-native-roots = ["librespot-core/rustls-tls-native-roots"] +rustls-tls-webpki-roots = ["librespot-core/rustls-tls-webpki-roots"] [dependencies] librespot-core.workspace = true diff --git a/oauth/Cargo.toml b/oauth/Cargo.toml index 88e042c9..31d58df0 100644 --- a/oauth/Cargo.toml +++ b/oauth/Cargo.toml @@ -14,7 +14,19 @@ default = ["native-tls"] # TLS backends (mutually exclusive - compile-time checks in src/lib.rs) native-tls = ["oauth2/native-tls", "reqwest/native-tls"] -rustls-tls = ["oauth2/rustls-tls", "reqwest/rustls-tls"] +rustls-tls-native-roots = [ + "__rustls", + "oauth2/rustls-tls", + "reqwest/rustls-tls-native-roots", +] +rustls-tls-webpki-roots = [ + "__rustls", + "oauth2/rustls-tls", + "reqwest/rustls-tls-webpki-roots", +] + +# Internal features - these are not meant to be used by end users +__rustls = [] [dependencies] log = "0.4" diff --git a/oauth/src/lib.rs b/oauth/src/lib.rs index 1bbe1188..70e83339 100644 --- a/oauth/src/lib.rs +++ b/oauth/src/lib.rs @@ -39,12 +39,14 @@ use url::Url; // The dependency chain is: workspace -> core -> oauth // So oauth's feature validation runs before core's, catching configuration errors quickly. -#[cfg(all(feature = "native-tls", feature = "rustls-tls"))] -compile_error!("Features 'native-tls' and 'rustls-tls' are mutually exclusive. Enable only one."); - -#[cfg(not(any(feature = "native-tls", feature = "rustls-tls")))] +#[cfg(all(feature = "native-tls", feature = "__rustls"))] compile_error!( - "Either feature \"native-tls\" (default) or \"rustls-tls\" must be enabled for this crate." + "Feature \"native-tls\" is mutually exclusive with \"rustls-tls-native-roots\" and \"rustls-tls-webpki-roots\". Enable only one." +); + +#[cfg(not(any(feature = "native-tls", feature = "__rustls")))] +compile_error!( + "Either feature \"native-tls\" (default), \"rustls-tls-native-roots\" or \"rustls-tls-webpki-roots\" must be enabled for this crate." ); /// Possible errors encountered during the OAuth authentication flow. diff --git a/playback/Cargo.toml b/playback/Cargo.toml index 6d98fac9..7f80d900 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -35,10 +35,15 @@ native-tls = [ "librespot-audio/native-tls", "librespot-metadata/native-tls", ] -rustls-tls = [ - "librespot-core/rustls-tls", - "librespot-audio/rustls-tls", - "librespot-metadata/rustls-tls", +rustls-tls-native-roots = [ + "librespot-core/rustls-tls-native-roots", + "librespot-audio/rustls-tls-native-roots", + "librespot-metadata/rustls-tls-native-roots", +] +rustls-tls-webpki-roots = [ + "librespot-core/rustls-tls-webpki-roots", + "librespot-audio/rustls-tls-webpki-roots", + "librespot-metadata/rustls-tls-webpki-roots", ] [dependencies]