Fix Store PostgreSQL channel binding (#3212)
The former behaviour to always use a SHA-256 hash of the server certificate for the tls-server-end-point channel binding was incorrect with regard to RFC5929, which specifies if the signature algorithm uses a single hash function, that hash function should be used, unless the signature algorithm uses MD5 or SHA-1, in which case the channel binding should use an SHA-256 hash. This change attempts to align with the behaviour of OpenSSL, used by PostgreSQL, in using the signature algorithm's hash function.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -8004,6 +8004,7 @@ dependencies = [
|
||||
"trc",
|
||||
"types",
|
||||
"utils",
|
||||
"x509-parser",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ tokio-rustls = { version = "0.26", optional = true, default-features = false, fe
|
||||
rustls = { version = "0.23.5", optional = true, default-features = false, features = ["std", "aws_lc_rs", "tls12"] }
|
||||
rustls-pki-types = { version = "1", optional = true }
|
||||
aws-lc-rs = { version = "1", optional = true }
|
||||
x509-parser = { version = "0.18", optional = true }
|
||||
bytes = { version = "1.10", optional = true }
|
||||
mysql_async = { version = "0.36", default-features = false, features = ["default-rustls", "minimal"], optional = true }
|
||||
serde_json = { version = "1.0.64" }
|
||||
@@ -61,7 +62,7 @@ tokio = { version = "1.47", features = ["full"] }
|
||||
# Data Stores
|
||||
rocks = ["rocksdb", "rayon", "num_cpus"]
|
||||
sqlite = ["rusqlite", "rayon", "r2d2", "num_cpus", "lru-cache"]
|
||||
postgres = ["tokio-postgres", "deadpool", "deadpool-postgres", "tokio-rustls", "rustls", "aws-lc-rs", "rustls-pki-types", "futures", "bytes"]
|
||||
postgres = ["tokio-postgres", "deadpool", "deadpool-postgres", "tokio-rustls", "rustls", "aws-lc-rs", "rustls-pki-types", "x509-parser", "futures", "bytes"]
|
||||
mysql = ["mysql_async", "futures"]
|
||||
foundation = ["foundationdb", "futures"]
|
||||
fdb-chunked-bm = []
|
||||
|
||||
@@ -22,6 +22,20 @@ use rustls_pki_types::ServerName;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_postgres::tls::{ChannelBinding, MakeTlsConnect, TlsConnect};
|
||||
use tokio_rustls::{TlsConnector, client::TlsStream};
|
||||
use x509_parser::{
|
||||
asn1_rs::oid,
|
||||
oid_registry::{
|
||||
OID_HASH_SHA1, OID_MD5_WITH_RSA, OID_NIST_HASH_SHA256, OID_NIST_HASH_SHA384,
|
||||
OID_NIST_HASH_SHA512, OID_PKCS1_MD5WITHRSAENC, OID_PKCS1_RSASSAPSS, OID_PKCS1_SHA1WITHRSA,
|
||||
OID_PKCS1_SHA224WITHRSA, OID_PKCS1_SHA256WITHRSA, OID_PKCS1_SHA384WITHRSA,
|
||||
OID_PKCS1_SHA512WITHRSA, OID_SHA1_WITH_RSA, OID_SIG_DSA_WITH_SHA1,
|
||||
OID_SIG_ECDSA_WITH_SHA224, OID_SIG_ECDSA_WITH_SHA256, OID_SIG_ECDSA_WITH_SHA384,
|
||||
OID_SIG_ECDSA_WITH_SHA512,
|
||||
},
|
||||
parse_x509_certificate,
|
||||
prelude::X509Certificate,
|
||||
signature_algorithm::RsaSsaPssParams,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MakeRustlsConnect {
|
||||
@@ -85,6 +99,47 @@ where
|
||||
|
||||
pub struct RustlsStream<S>(Pin<Box<TlsStream<S>>>);
|
||||
|
||||
fn cb_digest_for_cert(cert: &X509Certificate<'_>) -> Option<&'static digest::Algorithm> {
|
||||
let sig_alg = cert.signature_algorithm.oid();
|
||||
// Signature algorithms that use a digest should use the same digest for channel binding:
|
||||
if sig_alg == &OID_PKCS1_SHA512WITHRSA || sig_alg == &OID_SIG_ECDSA_WITH_SHA512 {
|
||||
Some(&digest::SHA512)
|
||||
} else if sig_alg == &OID_PKCS1_SHA384WITHRSA || sig_alg == &OID_SIG_ECDSA_WITH_SHA384 {
|
||||
Some(&digest::SHA384)
|
||||
} else if sig_alg == &OID_PKCS1_MD5WITHRSAENC
|
||||
|| sig_alg == &OID_MD5_WITH_RSA
|
||||
|| sig_alg == &OID_PKCS1_SHA1WITHRSA
|
||||
|| sig_alg == &OID_SHA1_WITH_RSA
|
||||
|| sig_alg == &OID_SIG_DSA_WITH_SHA1
|
||||
|| sig_alg == &OID_PKCS1_SHA256WITHRSA
|
||||
|| sig_alg == &OID_SIG_ECDSA_WITH_SHA256
|
||||
{
|
||||
// ...apart from MD5 or SHA1, which use SHA256 for channel binding, as per RFC 5929 section 4.1:
|
||||
Some(&digest::SHA256)
|
||||
} else if sig_alg == &OID_PKCS1_SHA224WITHRSA || sig_alg == &OID_SIG_ECDSA_WITH_SHA224 {
|
||||
Some(&digest::SHA224)
|
||||
} else if sig_alg == &OID_PKCS1_RSASSAPSS {
|
||||
// For RSASSA-PSS, the hash algorithm is specified in the parameters of the signature algorithm:
|
||||
let params_any = cert.signature_algorithm.parameters()?;
|
||||
let pss = RsaSsaPssParams::try_from(params_any).ok()?;
|
||||
let alg = pss.hash_algorithm_oid();
|
||||
if alg == &OID_NIST_HASH_SHA512 {
|
||||
Some(&digest::SHA512)
|
||||
} else if alg == &OID_NIST_HASH_SHA384 {
|
||||
Some(&digest::SHA384)
|
||||
} else if alg == &OID_NIST_HASH_SHA256 || alg == &OID_HASH_SHA1 {
|
||||
Some(&digest::SHA256)
|
||||
} else if alg == &oid!(2.16.840.1.101.3.4.2.4) {
|
||||
// id-sha224 from RFC 4055 ^
|
||||
Some(&digest::SHA224)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> tokio_postgres::tls::TlsStream for RustlsStream<S>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
@@ -92,10 +147,17 @@ where
|
||||
fn channel_binding(&self) -> ChannelBinding {
|
||||
let (_, session) = self.0.get_ref();
|
||||
match session.peer_certificates() {
|
||||
Some(certs) if !certs.is_empty() => {
|
||||
let sha256 = digest::digest(&digest::SHA256, certs[0].as_ref());
|
||||
ChannelBinding::tls_server_end_point(sha256.as_ref().into())
|
||||
}
|
||||
Some(certs) if !certs.is_empty() => match parse_x509_certificate(certs[0].as_ref()) {
|
||||
Ok((_, cert)) => {
|
||||
if let Some(digest_alg) = cb_digest_for_cert(&cert) {
|
||||
let dgst = digest::digest(digest_alg, certs[0].as_ref());
|
||||
ChannelBinding::tls_server_end_point(dgst.as_ref().into())
|
||||
} else {
|
||||
ChannelBinding::none()
|
||||
}
|
||||
}
|
||||
Err(_) => ChannelBinding::none(),
|
||||
},
|
||||
_ => ChannelBinding::none(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user