Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,37 @@ description = "Rustls integration for tokio-postgres"
version = "0.13.0"
authors = ["Jasper Hugo <jasper@jasperhugo.com>"]
repository = "https://github.com/jbg/tokio-postgres-rustls"
edition = "2018"
edition = "2024"
license = "MIT"
readme = "README.md"

[dependencies]
const-oid = { version = "0.9.6", default-features = false, features = ["db"] }
ring = { version = "0.17", default-features = false }
rustls = { version = "0.23", default-features = false }
rustls = { version = "0.23", default-features = false, features = ["tls12"] }
tokio-rustls = { version = "0.26", default-features = false, features = [
"tls12",
] }
tokio = { version = "1", default-features = false }
tokio-postgres = { version = "0.7", default-features = false }
tokio-rustls = { version = "0.26", default-features = false }
tokio-postgres = { version = "0.7", default-features = false, features = [
"runtime",
] }
x509-cert = { version = "0.2.5", default-features = false, features = ["std"] }
aws-lc-rs = { version = "1.15", optional = true }
ring = { version = "0.17.14", optional = true }
webpki-roots = { version = "1.0", optional = true }
rustls-native-certs = { version = "0.8.2", optional = true }

[features]
default = ["aws-lc-rs", "webpki-roots"]
aws-lc-rs = [
"rustls/aws-lc-rs",
"tokio-rustls/aws-lc-rs",
"rustls/prefer-post-quantum",
"dep:aws-lc-rs",
]
ring = ["rustls/ring", "tokio-rustls/ring", "dep:ring"]
webpki-roots = ["dep:webpki-roots"]
rustls-native-certs = ["dep:rustls-native-certs"]

[dev-dependencies]
env_logger = { version = "0.11", default-features = false }
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# tokio-postgres-rustls

This is an integration between the [rustls TLS stack](https://github.com/ctz/rustls)
and the [tokio-postgres asynchronous PostgreSQL client library](https://github.com/sfackler/rust-postgres).

Expand All @@ -8,6 +9,16 @@ and the [tokio-postgres asynchronous PostgreSQL client library](https://github.c

# Example

Using default certs by enabling one of `webpki-roots` or `rustls-native-certs`:

```
let tls = tokio_postgres_rustls::MakeRustlsConnect::default();
let connect_fut = tokio_postgres::connect("sslmode=require host=localhost user=postgres", tls);
// ...
```

Or providing certs:

```
let config = rustls::ClientConfig::builder()
.with_root_certificates(rustls::RootCertStore::empty())
Expand All @@ -18,4 +29,5 @@ let connect_fut = tokio_postgres::connect("sslmode=require host=localhost user=p
```

# License

tokio-postgres-rustls is distributed under the MIT license.
87 changes: 68 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@
#![forbid(missing_docs, unsafe_code)]
#![warn(clippy::all, clippy::pedantic)]

#[cfg(not(any(feature = "aws-lc-rs", feature = "ring")))]
compile_error!("Either 'aws-lc-rs' or 'ring' feature must be enabled");

#[cfg(all(feature = "aws-lc-rs", feature = "ring"))]
compile_error!("Only one of 'aws-lc-rs' or 'ring' features can be enabled at a time");

use std::{convert::TryFrom, sync::Arc};

use rustls::{pki_types::ServerName, ClientConfig};
use rustls::{ClientConfig, pki_types::ServerName};
use tokio::io::{AsyncRead, AsyncWrite};
use tokio_postgres::tls::MakeTlsConnect;

#[cfg(feature = "aws-lc-rs")]
use rustls::crypto::aws_lc_rs as crypto_provider;

#[cfg(feature = "ring")]
use rustls::crypto::ring as crypto_provider;

mod private {
use std::{
future::Future,
io,
pin::Pin,
task::{Context, Poll},
};
#[cfg(feature = "aws-lc-rs")]
use aws_lc_rs::digest::{self as crypto_digest, SHA256, SHA384, SHA512};

#[cfg(feature = "ring")]
use ring::digest::{self as crypto_digest, SHA256, SHA384, SHA512};

use const_oid::db::{
rfc5912::{
Expand All @@ -25,12 +36,17 @@ mod private {
},
rfc8410::ID_ED_25519,
};
use ring::digest;
use rustls::pki_types::ServerName;
use std::{
future::Future,
io,
pin::Pin,
task::{Context, Poll},
};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio_postgres::tls::{ChannelBinding, TlsConnect};
use tokio_rustls::{client::TlsStream, TlsConnector};
use x509_cert::{der::Decode, Certificate};
use tokio_rustls::{TlsConnector, client::TlsStream};
use x509_cert::{Certificate, der::Decode};

pub struct TlsConnectFuture<S> {
inner: tokio_rustls::Connect<S>,
Expand Down Expand Up @@ -87,20 +103,18 @@ mod private {
| ID_SHA_256
| SHA_1_WITH_RSA_ENCRYPTION
| SHA_256_WITH_RSA_ENCRYPTION
| ECDSA_WITH_SHA_256 => &digest::SHA256,
| ECDSA_WITH_SHA_256 => &SHA256,
ID_SHA_384 | SHA_384_WITH_RSA_ENCRYPTION | ECDSA_WITH_SHA_384 => {
&digest::SHA384
}
ID_SHA_512 | SHA_512_WITH_RSA_ENCRYPTION | ID_ED_25519 => {
&digest::SHA512
&SHA384
}
ID_SHA_512 | SHA_512_WITH_RSA_ENCRYPTION | ID_ED_25519 => &SHA512,
_ => return None,
};

Some(digest)
})
.map_or_else(ChannelBinding::none, |algorithm| {
let hash = digest::digest(algorithm, certs[0].as_ref());
let hash = crypto_digest::digest(algorithm, certs[0].as_ref());
ChannelBinding::tls_server_end_point(hash.as_ref().into())
}),
_ => ChannelBinding::none(),
Expand Down Expand Up @@ -167,6 +181,12 @@ impl MakeRustlsConnect {
}
}

impl Default for MakeRustlsConnect {
fn default() -> Self {
Self::new(default_config())
}
}

impl<S> MakeTlsConnect<S> for MakeRustlsConnect
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
Expand All @@ -185,14 +205,43 @@ where
}
}

fn default_config() -> rustls::ClientConfig {
let provider = crypto_provider::default_provider();

rustls::ClientConfig::builder_with_provider(Arc::new(provider))
.with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
.expect("TLS versions supported by provider")
.with_root_certificates(root_certificates())
.with_no_client_auth()
}

fn root_certificates() -> rustls::RootCertStore {
#[allow(unused_mut)]
let mut roots = rustls::RootCertStore::empty();

#[cfg(feature = "webpki-roots")]
{
roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
}

#[cfg(feature = "rustls-native-certs")]
{
let certs = rustls_native_certs::load_native_certs();
for cert in certs.certs {
let _ = roots.add(cert);
}
}

roots
}

#[cfg(test)]
mod tests {
use super::*;
use rustls::pki_types::{CertificateDer, UnixTime};
use rustls::{
client::danger::ServerCertVerifier,
client::danger::{HandshakeSignatureValid, ServerCertVerified},
Error, SignatureScheme,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
pki_types::{CertificateDer, UnixTime},
};

#[derive(Debug)]
Expand Down