LCOV - code coverage report
Current view: top level - proxy/src/scram - password.rs (source / functions) Coverage Total Hit
Test: 8ac049b474321fdc72ddcb56d7165153a1a900e8.info Lines: 100.0 % 40 40
Test Date: 2023-09-06 10:18:01 Functions: 100.0 % 7 7

            Line data    Source code
       1              : //! Password hashing routines.
       2              : 
       3              : use super::key::ScramKey;
       4              : 
       5              : pub const SALTED_PASSWORD_LEN: usize = 32;
       6              : 
       7              : /// Salted hashed password is essential for [key](super::key) derivation.
       8              : #[repr(transparent)]
       9              : pub struct SaltedPassword {
      10              :     bytes: [u8; SALTED_PASSWORD_LEN],
      11              : }
      12              : 
      13              : impl SaltedPassword {
      14              :     /// See `scram-common.c : scram_SaltedPassword` for details.
      15              :     /// Further reading: <https://datatracker.ietf.org/doc/html/rfc2898> (see `PBKDF2`).
      16            6 :     pub fn new(password: &[u8], salt: &[u8], iterations: u32) -> SaltedPassword {
      17            6 :         pbkdf2::pbkdf2_hmac_array::<sha2::Sha256, 32>(password, salt, iterations).into()
      18            6 :     }
      19              : 
      20              :     /// Derive `ClientKey` from a salted hashed password.
      21            5 :     pub fn client_key(&self) -> ScramKey {
      22            5 :         super::hmac_sha256(&self.bytes, [b"Client Key".as_ref()]).into()
      23            5 :     }
      24              : 
      25              :     /// Derive `ServerKey` from a salted hashed password.
      26            5 :     pub fn server_key(&self) -> ScramKey {
      27            5 :         super::hmac_sha256(&self.bytes, [b"Server Key".as_ref()]).into()
      28            5 :     }
      29              : }
      30              : 
      31              : impl From<[u8; SALTED_PASSWORD_LEN]> for SaltedPassword {
      32              :     #[inline(always)]
      33            7 :     fn from(bytes: [u8; SALTED_PASSWORD_LEN]) -> Self {
      34            7 :         Self { bytes }
      35            7 :     }
      36              : }
      37              : 
      38              : #[cfg(test)]
      39              : mod tests {
      40              :     use super::SaltedPassword;
      41              : 
      42            1 :     fn legacy_pbkdf2_impl(password: &[u8], salt: &[u8], iterations: u32) -> SaltedPassword {
      43            1 :         let one = 1_u32.to_be_bytes(); // magic
      44            1 : 
      45            1 :         let mut current = super::super::hmac_sha256(password, [salt, &one]);
      46            1 :         let mut result = current;
      47            1 :         for _ in 1..iterations {
      48         4095 :             current = super::super::hmac_sha256(password, [current.as_ref()]);
      49              :             // TODO: result = current.zip(result).map(|(x, y)| x ^ y), issue #80094
      50       131040 :             for (i, x) in current.iter().enumerate() {
      51       131040 :                 result[i] ^= x;
      52       131040 :             }
      53              :         }
      54              : 
      55            1 :         result.into()
      56            1 :     }
      57              : 
      58            1 :     #[test]
      59            1 :     fn pbkdf2() {
      60            1 :         let password = "a-very-secure-password";
      61            1 :         let salt = "such-a-random-salt";
      62            1 :         let iterations = 4096;
      63            1 :         let output = [
      64            1 :             203, 18, 206, 81, 4, 154, 193, 100, 147, 41, 211, 217, 177, 203, 69, 210, 194, 211,
      65            1 :             101, 1, 248, 156, 96, 0, 8, 223, 30, 87, 158, 41, 20, 42,
      66            1 :         ];
      67            1 : 
      68            1 :         let actual = SaltedPassword::new(password.as_bytes(), salt.as_bytes(), iterations);
      69            1 :         let expected = legacy_pbkdf2_impl(password.as_bytes(), salt.as_bytes(), iterations);
      70            1 : 
      71            1 :         assert_eq!(actual.bytes, output);
      72            1 :         assert_eq!(actual.bytes, expected.bytes);
      73            1 :     }
      74              : }
        

Generated by: LCOV version 2.1-beta