Skip to content

Commit

Permalink
Added rustls support via feature flag, resolves #33
Browse files Browse the repository at this point in the history
  • Loading branch information
ancwrd1 committed Oct 27, 2024
1 parent f2dd2a2 commit 01df654
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 5.1.0
- Added `rustls` backend support via two new feature flags: `client-rustls` and `async-client-rustls`
- Only `async-client-tls` feature is enabled by default

## 5.0.5
- Added IPP attributes required by Windows Print Protection mode

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [ "ipp", "util", "examples" ]
resolver = "2"

[workspace.package]
version = "5.0.5"
version = "5.1.0"
authors = ["Dmitry Pankratov <[email protected]>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/ancwrd1/ipp.rs"
Expand Down
3 changes: 1 addition & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ path = "src/print-job.rs"
name = "print-job"

[dependencies]
ipp = { path = "../ipp", version = "5.0.5" }
ipp = { path = "../ipp", version = "5.1.0", features = ["client-tls"] }
tokio = { version = "1", features = ["macros", "fs", "rt-multi-thread"] }
tokio-util = { version = "0.7", features = ["compat"] }

12 changes: 11 additions & 1 deletion ipp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ serde = { version = "1", optional = true, features = ["derive"] }
ureq = { version = "2", default-features = false, optional = true }
native-tls = { version = "0.2", optional = true }
base64 = { version = "0.22", optional = true }
rustls-native-certs = { version = "0.8", optional = true }

[dependencies.futures-util]
version = "0.3"
Expand All @@ -39,6 +40,13 @@ optional = true
default-features = false
features = ["stream"]

[dependencies.rustls]
version = "0.23"
optional = true
default-features = false
features = ["ring", "log", "tls12", "std"]


[dependencies.tokio-util]
version = "0.7"
optional = true
Expand All @@ -48,10 +56,12 @@ features = ["io", "compat"]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

[features]
default = ["async-client", "client", "async-client-tls", "client-tls"]
default = ["async-client-tls"]
serde = ["dep:serde", "bytes/serde"]
async = ["futures-util", "futures-executor"]
async-client = ["async", "reqwest", "tokio-util", "base64"]
client = ["ureq", "base64"]
async-client-tls = ["async-client", "native-tls", "reqwest/native-tls"]
client-tls = ["client", "native-tls", "ureq/native-tls"]
async-client-rustls = ["async-client", "rustls", "reqwest/rustls-tls-native-roots"]
client-rustls = ["client", "rustls", "rustls-native-certs", "ureq/tls"]
21 changes: 12 additions & 9 deletions ipp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ It supports both synchronous and asynchronous operations (requests and responses

The following build-time features are supported:

* `async` - enables asynchronous APIs
* `async-client` - enables asynchronous IPP client based on reqwest crate, implies `async` feature
* `client` - enables blocking IPP client based on ureq crate
* `async-client-tls` - enables asynchronous IPP client with TLS
* `client-tls` - enables blocking IPP client with TLS

By default, all features are enabled. Use `default-features=false` dependency option to disable them.

[Documentation](https://ancwrd1.github.io/ipp.rs/doc/ipp/)
* `async` - enables asynchronous APIs.
* `async-client` - enables asynchronous IPP client based on `reqwest` crate, implies `async` feature.
* `client` - enables blocking IPP client based on `ureq` crate.
* `async-client-tls` - enables asynchronous IPP client with TLS, using native-tls backend. Implies `async-client` feature.
* `client-tls` - enables blocking IPP client with TLS, using native-tls backend. Implies `client` feature.
* `async-client-rustls` - enables asynchronous IPP client with TLS, using rustls backend. Implies `async-client` feature.
* `client-rustls` - enables blocking IPP client with TLS, using rustls backend. Implies `client` feature.

By default, the following features are enabled: `async-client-tls`.
Use `default-features=false` dependency option to disable them.

[Documentation](https://docs.rs/ipp/latest/ipp/)

Usage example for async client:

Expand Down
84 changes: 83 additions & 1 deletion ipp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ pub mod non_blocking {
builder = builder.timeout(timeout);
}

#[cfg(feature = "async-client-tls")]
#[cfg(any(feature = "async-client-tls", feature = "async-client-rustls"))]
{
if self.0.ignore_tls_errors {
builder = builder
Expand All @@ -172,6 +172,11 @@ pub mod non_blocking {
}
}

#[cfg(feature = "async-client-rustls")]
{
builder = builder.use_rustls_tls();
}

let mut req_builder = builder
.user_agent(USER_AGENT)
.build()?
Expand Down Expand Up @@ -265,6 +270,37 @@ pub mod blocking {
builder = builder.tls_connector(std::sync::Arc::new(tls_connector));
}

#[cfg(feature = "client-rustls")]
{
use rustls::pki_types::pem::PemObject;
let mut root_store = rustls::RootCertStore::empty();
let certs = rustls_native_certs::load_native_certs();
root_store.add_parsable_certificates(certs.certs);

for data in &self.0.ca_certs {
let cert = rustls::pki_types::CertificateDer::<'static>::from_pem_slice(data)
.unwrap_or_else(|_| rustls::pki_types::CertificateDer::from_slice(data));
root_store.add(cert)?;
}

let secure_config = rustls::ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();

let config = if self.0.ignore_tls_errors {
rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(std::sync::Arc::new(verifiers::NoVerifier(
secure_config.crypto_provider().clone(),
)))
.with_no_client_auth()
} else {
secure_config
};

builder = builder.tls_config(std::sync::Arc::new(config));
}

let agent = builder.user_agent(USER_AGENT).build();

let mut req = agent
Expand All @@ -282,6 +318,52 @@ pub mod blocking {
parser.parse().map_err(IppError::from)
}
}

#[cfg(feature = "client-rustls")]
mod verifiers {
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use rustls::{crypto::CryptoProvider, DigitallySignedStruct, Error, SignatureScheme};
use std::sync::Arc;

#[derive(Debug)]
pub struct NoVerifier(pub Arc<CryptoProvider>);

impl ServerCertVerifier for NoVerifier {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer,
_intermediates: &[CertificateDer],
_server_name: &ServerName,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, Error> {
Ok(ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Ok(HandshakeSignatureValid::assertion())
}

fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Ok(HandshakeSignatureValid::assertion())
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}
}
}

#[cfg(test)]
Expand Down
5 changes: 5 additions & 0 deletions ipp/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,9 @@ pub enum IppError {
#[cfg(any(feature = "async-client-tls", feature = "client-tls"))]
/// TLS error
TlsError(#[from] native_tls::Error),

#[error(transparent)]
#[cfg(feature = "client-rustls")]
/// TLS error
RustlsError(#[from] rustls::Error),
}
16 changes: 8 additions & 8 deletions ipp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
//! * using any third-party HTTP client and send the serialized request manually.
//!
//! This crate supports both synchronous and asynchronous operations. The following feature flags are supported:
//! * `async` - enables asynchronous APIs
//! * `async-client` - enables asynchronous IPP client based on `reqwest` crate, implies `async` feature
//! * `client` - enables blocking IPP client based on `ureq` crate
//! * `async-client-tls` - enables asynchronous IPP client with TLS support
//! * `client-tls` - enables blocking IPP client with TLS support
//!
//! By default, all features are enabled.
//! * `async` - enables asynchronous APIs.
//! * `async-client` - enables asynchronous IPP client based on `reqwest` crate, implies `async` feature.
//! * `client` - enables blocking IPP client based on `ureq` crate.
//! * `async-client-tls` - enables asynchronous IPP client with TLS, using native-tls backend. Implies `async-client` feature.
//! * `client-tls` - enables blocking IPP client with TLS, using native-tls backend. Implies `client` feature.
//! * `async-client-rustls` - enables asynchronous IPP client with TLS, using rustls backend. Implies `async-client` feature.
//! * `client-rustls` - enables blocking IPP client with TLS, using rustls backend. Implies `client` feature.
//!
//! By default, the following feature is enabled: `async-client-tls`.
//!
//! Implementation notes:
//! * all RFC IPP values are supported including arrays and collections, for both de- and serialization.
//! * this crate is also suitable for building IPP servers, however the example is not provided yet.
//! * some operations (e.g. CUPS-specific) require authorization which can be supplied in the printer URI.
//!
//! Usage examples:
//!
Expand Down
2 changes: 1 addition & 1 deletion util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ name = "ipputil"
path = "src/main.rs"

[dependencies]
ipp = { path = "../ipp", version = "5.0.5", default-features = false, features = ["client-tls"] }
ipp = { path = "../ipp", version = "5.1.0", default-features = false, features = ["client-tls"] }
clap = { version = "4", features = ["derive"] }

0 comments on commit 01df654

Please sign in to comment.