LCOV - code coverage report
Current view: top level - proxy/src/scram - secret.rs (source / functions) Coverage Total Hit
Test: 322b88762cba8ea666f63cda880cccab6936bf37.info Lines: 100.0 % 54 54
Test Date: 2024-02-29 11:57:12 Functions: 87.5 % 8 7

            Line data    Source code
       1              : //! Tools for SCRAM server secret management.
       2              : 
       3              : use super::base64_decode_array;
       4              : use super::key::ScramKey;
       5              : 
       6              : /// Server secret is produced from user's password,
       7              : /// and is used throughout the authentication process.
       8           24 : #[derive(Clone, Eq, PartialEq, Debug)]
       9              : pub struct ServerSecret {
      10              :     /// Number of iterations for `PBKDF2` function.
      11              :     pub iterations: u32,
      12              :     /// Salt used to hash user's password.
      13              :     pub salt_base64: String,
      14              :     /// Hashed `ClientKey`.
      15              :     pub stored_key: ScramKey,
      16              :     /// Used by client to verify server's signature.
      17              :     pub server_key: ScramKey,
      18              :     /// Should auth fail no matter what?
      19              :     /// This is exactly the case for mocked secrets.
      20              :     pub doomed: bool,
      21              : }
      22              : 
      23              : impl ServerSecret {
      24           30 :     pub fn parse(input: &str) -> Option<Self> {
      25              :         // SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
      26           30 :         let s = input.strip_prefix("SCRAM-SHA-256$")?;
      27           30 :         let (params, keys) = s.split_once('$')?;
      28              : 
      29           30 :         let ((iterations, salt), (stored_key, server_key)) =
      30           30 :             params.split_once(':').zip(keys.split_once(':'))?;
      31              : 
      32           30 :         let secret = ServerSecret {
      33           30 :             iterations: iterations.parse().ok()?,
      34           30 :             salt_base64: salt.to_owned(),
      35           30 :             stored_key: base64_decode_array(stored_key)?.into(),
      36           30 :             server_key: base64_decode_array(server_key)?.into(),
      37           30 :             doomed: false,
      38           30 :         };
      39           30 : 
      40           30 :         Some(secret)
      41           30 :     }
      42              : 
      43              :     /// To avoid revealing information to an attacker, we use a
      44              :     /// mocked server secret even if the user doesn't exist.
      45              :     /// See `auth-scram.c : mock_scram_secret` for details.
      46           14 :     pub fn mock(user: &str, nonce: [u8; 32]) -> Self {
      47           14 :         // Refer to `auth-scram.c : scram_mock_salt`.
      48           14 :         let mocked_salt = super::sha256([user.as_bytes(), &nonce]);
      49           14 : 
      50           14 :         Self {
      51           14 :             iterations: 4096,
      52           14 :             salt_base64: base64::encode(mocked_salt),
      53           14 :             stored_key: ScramKey::default(),
      54           14 :             server_key: ScramKey::default(),
      55           14 :             doomed: true,
      56           14 :         }
      57           14 :     }
      58              : 
      59              :     /// Build a new server secret from the prerequisites.
      60              :     /// XXX: We only use this function in tests.
      61              :     #[cfg(test)]
      62           26 :     pub fn build(password: &str) -> Option<Self> {
      63           26 :         Self::parse(&postgres_protocol::password::scram_sha_256(
      64           26 :             password.as_bytes(),
      65           26 :         ))
      66           26 :     }
      67              : }
      68              : 
      69              : #[cfg(test)]
      70              : mod tests {
      71              :     use super::*;
      72              : 
      73            2 :     #[test]
      74            2 :     fn parse_scram_secret() {
      75            2 :         let iterations = 4096;
      76            2 :         let salt = "+/tQQax7twvwTj64mjBsxQ==";
      77            2 :         let stored_key = "D5h6KTMBlUvDJk2Y8ELfC1Sjtc6k9YHjRyuRZyBNJns=";
      78            2 :         let server_key = "Pi3QHbcluX//NDfVkKlFl88GGzlJ5LkyPwcdlN/QBvI=";
      79            2 : 
      80            2 :         let secret = format!(
      81            2 :             "SCRAM-SHA-256${iterations}:{salt}${stored_key}:{server_key}",
      82            2 :             iterations = iterations,
      83            2 :             salt = salt,
      84            2 :             stored_key = stored_key,
      85            2 :             server_key = server_key,
      86            2 :         );
      87            2 : 
      88            2 :         let parsed = ServerSecret::parse(&secret).unwrap();
      89            2 :         assert_eq!(parsed.iterations, iterations);
      90            2 :         assert_eq!(parsed.salt_base64, salt);
      91              : 
      92            2 :         assert_eq!(base64::encode(parsed.stored_key), stored_key);
      93            2 :         assert_eq!(base64::encode(parsed.server_key), server_key);
      94            2 :     }
      95              : }
        

Generated by: LCOV version 2.1-beta