LCOV - code coverage report
Current view: top level - proxy/src/scram - password.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 100.0 % 40 40
Test Date: 2024-02-07 07:37:29 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           28 :     pub fn new(password: &[u8], salt: &[u8], iterations: u32) -> SaltedPassword {
      17           28 :         pbkdf2::pbkdf2_hmac_array::<sha2::Sha256, 32>(password, salt, iterations).into()
      18           28 :     }
      19              : 
      20              :     /// Derive `ClientKey` from a salted hashed password.
      21           26 :     pub fn client_key(&self) -> ScramKey {
      22           26 :         super::hmac_sha256(&self.bytes, [b"Client Key".as_ref()]).into()
      23           26 :     }
      24              : 
      25              :     /// Derive `ServerKey` from a salted hashed password.
      26           26 :     pub fn server_key(&self) -> ScramKey {
      27           26 :         super::hmac_sha256(&self.bytes, [b"Server Key".as_ref()]).into()
      28           26 :     }
      29              : }
      30              : 
      31              : impl From<[u8; SALTED_PASSWORD_LEN]> for SaltedPassword {
      32              :     #[inline(always)]
      33           30 :     fn from(bytes: [u8; SALTED_PASSWORD_LEN]) -> Self {
      34           30 :         Self { bytes }
      35           30 :     }
      36              : }
      37              : 
      38              : #[cfg(test)]
      39              : mod tests {
      40              :     use super::SaltedPassword;
      41              : 
      42            2 :     fn legacy_pbkdf2_impl(password: &[u8], salt: &[u8], iterations: u32) -> SaltedPassword {
      43            2 :         let one = 1_u32.to_be_bytes(); // magic
      44            2 : 
      45            2 :         let mut current = super::super::hmac_sha256(password, [salt, &one]);
      46            2 :         let mut result = current;
      47            2 :         for _ in 1..iterations {
      48         8190 :             current = super::super::hmac_sha256(password, [current.as_ref()]);
      49              :             // TODO: result = current.zip(result).map(|(x, y)| x ^ y), issue #80094
      50       262080 :             for (i, x) in current.iter().enumerate() {
      51       262080 :                 result[i] ^= x;
      52       262080 :             }
      53              :         }
      54              : 
      55            2 :         result.into()
      56            2 :     }
      57              : 
      58            2 :     #[test]
      59            2 :     fn pbkdf2() {
      60            2 :         let password = "a-very-secure-password";
      61            2 :         let salt = "such-a-random-salt";
      62            2 :         let iterations = 4096;
      63            2 :         let output = [
      64            2 :             203, 18, 206, 81, 4, 154, 193, 100, 147, 41, 211, 217, 177, 203, 69, 210, 194, 211,
      65            2 :             101, 1, 248, 156, 96, 0, 8, 223, 30, 87, 158, 41, 20, 42,
      66            2 :         ];
      67            2 : 
      68            2 :         let actual = SaltedPassword::new(password.as_bytes(), salt.as_bytes(), iterations);
      69            2 :         let expected = legacy_pbkdf2_impl(password.as_bytes(), salt.as_bytes(), iterations);
      70            2 : 
      71            2 :         assert_eq!(actual.bytes, output);
      72            2 :         assert_eq!(actual.bytes, expected.bytes);
      73            2 :     }
      74              : }
        

Generated by: LCOV version 2.1-beta