LCOV - code coverage report
Current view: top level - proxy/src/tls - mod.rs (source / functions) Coverage Total Hit
Test: 1e20c4f2b28aa592527961bb32170ebbd2c9172f.info Lines: 88.9 % 18 16
Test Date: 2025-07-16 12:29:03 Functions: 100.0 % 2 2

            Line data    Source code
       1              : pub mod client_config;
       2              : pub mod postgres_rustls;
       3              : pub mod server_config;
       4              : 
       5              : use anyhow::Context;
       6              : use base64::Engine as _;
       7              : use base64::prelude::BASE64_STANDARD;
       8              : use rustls::pki_types::CertificateDer;
       9              : use sha2::{Digest, Sha256};
      10              : use tracing::{error, info};
      11              : use x509_cert::der::{Reader, SliceReader, oid};
      12              : 
      13              : /// <https://github.com/postgres/postgres/blob/ca481d3c9ab7bf69ff0c8d71ad3951d407f6a33c/src/include/libpq/pqcomm.h#L159>
      14              : pub const PG_ALPN_PROTOCOL: &[u8] = b"postgresql";
      15              : 
      16              : /// Channel binding parameter
      17              : ///
      18              : /// <https://www.rfc-editor.org/rfc/rfc5929#section-4>
      19              : /// Description: The hash of the TLS server's certificate as it
      20              : /// appears, octet for octet, in the server's Certificate message.  Note
      21              : /// that the Certificate message contains a certificate_list, in which
      22              : /// the first element is the server's certificate.
      23              : ///
      24              : /// The hash function is to be selected as follows:
      25              : ///
      26              : /// * if the certificate's signatureAlgorithm uses a single hash
      27              : ///   function, and that hash function is either MD5 or SHA-1, then use SHA-256;
      28              : ///
      29              : /// * if the certificate's signatureAlgorithm uses a single hash
      30              : ///   function and that hash function neither MD5 nor SHA-1, then use
      31              : ///   the hash function associated with the certificate's
      32              : ///   signatureAlgorithm;
      33              : ///
      34              : /// * if the certificate's signatureAlgorithm uses no hash functions or
      35              : ///   uses multiple hash functions, then this channel binding type's
      36              : ///   channel bindings are undefined at this time (updates to is channel
      37              : ///   binding type may occur to address this issue if it ever arises).
      38              : #[derive(Debug, Clone, Copy)]
      39              : pub enum TlsServerEndPoint {
      40              :     Sha256([u8; 32]),
      41              :     Undefined,
      42              : }
      43              : 
      44              : impl TlsServerEndPoint {
      45           33 :     pub fn new(cert: &CertificateDer<'_>) -> anyhow::Result<Self> {
      46              :         const SHA256_OIDS: &[oid::ObjectIdentifier] = &[
      47              :             // I'm explicitly not adding MD5 or SHA1 here... They're bad.
      48              :             oid::db::rfc5912::ECDSA_WITH_SHA_256,
      49              :             oid::db::rfc5912::SHA_256_WITH_RSA_ENCRYPTION,
      50              :         ];
      51              : 
      52           33 :         let certificate = SliceReader::new(cert)
      53           33 :             .context("Failed to parse cerficiate")?
      54           33 :             .decode::<x509_cert::Certificate>()
      55           33 :             .context("Failed to parse cerficiate")?;
      56              : 
      57           33 :         let subject = certificate.tbs_certificate.subject;
      58           33 :         info!(%subject, "parsing TLS certificate");
      59              : 
      60           33 :         let oid = certificate.signature_algorithm.oid;
      61           33 :         if SHA256_OIDS.contains(&oid) {
      62           33 :             let tls_server_end_point: [u8; 32] = Sha256::new().chain_update(cert).finalize().into();
      63           33 :             info!(%subject, tls_server_end_point = %BASE64_STANDARD.encode(tls_server_end_point), "determined channel binding");
      64           33 :             Ok(Self::Sha256(tls_server_end_point))
      65              :         } else {
      66            0 :             error!(%subject, "unknown channel binding");
      67            0 :             Ok(Self::Undefined)
      68              :         }
      69           33 :     }
      70              : 
      71           14 :     pub fn supported(&self) -> bool {
      72           14 :         !matches!(self, TlsServerEndPoint::Undefined)
      73           14 :     }
      74              : }
        

Generated by: LCOV version 2.1-beta