LCOV - code coverage report
Current view: top level - proxy/src/scram - signature.rs (source / functions) Coverage Total Hit
Test: 1d5975439f3c9882b18414799141ebf9a3922c58.info Lines: 88.0 % 25 22
Test Date: 2025-07-31 15:59:03 Functions: 75.0 % 4 3

            Line data    Source code
       1              : //! Tools for client/server signature management.
       2              : 
       3              : use hmac::Mac as _;
       4              : 
       5              : use super::key::{SCRAM_KEY_LEN, ScramKey};
       6              : use crate::metrics::Metrics;
       7              : use crate::scram::pbkdf2::Prf;
       8              : 
       9              : /// A collection of message parts needed to derive the client's signature.
      10              : #[derive(Debug)]
      11              : pub(crate) struct SignatureBuilder<'a> {
      12              :     pub(crate) client_first_message_bare: &'a str,
      13              :     pub(crate) server_first_message: &'a str,
      14              :     pub(crate) client_final_message_without_proof: &'a str,
      15              : }
      16              : 
      17              : impl SignatureBuilder<'_> {
      18           15 :     pub(crate) fn build(&self, key: &ScramKey) -> Signature {
      19              :         // don't know exactly. this is a rough approx
      20           15 :         Metrics::get().proxy.sha_rounds.inc_by(8);
      21              : 
      22           15 :         let mut mac = Prf::new_from_slice(key.as_ref()).expect("HMAC accepts all key sizes");
      23           15 :         mac.update(self.client_first_message_bare.as_bytes());
      24           15 :         mac.update(b",");
      25           15 :         mac.update(self.server_first_message.as_bytes());
      26           15 :         mac.update(b",");
      27           15 :         mac.update(self.client_final_message_without_proof.as_bytes());
      28           15 :         Signature {
      29           15 :             bytes: mac.finalize().into_bytes().into(),
      30           15 :         }
      31           15 :     }
      32              : }
      33              : 
      34              : /// A computed value which, when xored with `ClientProof`,
      35              : /// produces `ClientKey` that we need for authentication.
      36              : #[derive(Debug)]
      37              : #[repr(transparent)]
      38              : pub(crate) struct Signature {
      39              :     bytes: [u8; SCRAM_KEY_LEN],
      40              : }
      41              : 
      42              : impl Signature {
      43              :     /// Derive `ClientKey` from client's signature and proof.
      44            8 :     pub(crate) fn derive_client_key(&self, proof: &[u8; SCRAM_KEY_LEN]) -> ScramKey {
      45              :         // This is how the proof is calculated:
      46              :         //
      47              :         // 1. sha256(ClientKey) -> StoredKey
      48              :         // 2. hmac_sha256(StoredKey, [messages...]) -> ClientSignature
      49              :         // 3. ClientKey ^ ClientSignature -> ClientProof
      50              :         //
      51              :         // Step 3 implies that we can restore ClientKey from the proof
      52              :         // by xoring the latter with the ClientSignature. Afterwards we
      53              :         // can check that the presumed ClientKey meets our expectations.
      54            8 :         let mut signature = self.bytes;
      55          256 :         for (i, x) in proof.iter().enumerate() {
      56          256 :             signature[i] ^= x;
      57          256 :         }
      58              : 
      59            8 :         signature.into()
      60            8 :     }
      61              : }
      62              : 
      63              : impl From<[u8; SCRAM_KEY_LEN]> for Signature {
      64            0 :     fn from(bytes: [u8; SCRAM_KEY_LEN]) -> Self {
      65            0 :         Self { bytes }
      66            0 :     }
      67              : }
      68              : 
      69              : impl AsRef<[u8]> for Signature {
      70            7 :     fn as_ref(&self) -> &[u8] {
      71            7 :         &self.bytes
      72            7 :     }
      73              : }
        

Generated by: LCOV version 2.1-beta