Line data Source code
1 : //! Tools for client/server/stored key management.
2 :
3 : use hmac::Mac as _;
4 : use sha2::Digest as _;
5 : use subtle::ConstantTimeEq;
6 : use zeroize::Zeroize as _;
7 :
8 : use crate::metrics::Metrics;
9 : use crate::scram::pbkdf2::Prf;
10 :
11 : /// Faithfully taken from PostgreSQL.
12 : pub(crate) const SCRAM_KEY_LEN: usize = 32;
13 :
14 : /// One of the keys derived from the user's password.
15 : /// We use the same structure for all keys, i.e.
16 : /// `ClientKey`, `StoredKey`, and `ServerKey`.
17 : #[derive(Clone, Default, Eq, Debug)]
18 : #[repr(transparent)]
19 : pub(crate) struct ScramKey {
20 : bytes: [u8; SCRAM_KEY_LEN],
21 : }
22 :
23 : impl Drop for ScramKey {
24 106 : fn drop(&mut self) {
25 106 : self.bytes.zeroize();
26 106 : }
27 : }
28 :
29 : impl PartialEq for ScramKey {
30 2 : fn eq(&self, other: &Self) -> bool {
31 2 : self.ct_eq(other).into()
32 2 : }
33 : }
34 :
35 : impl ConstantTimeEq for ScramKey {
36 18 : fn ct_eq(&self, other: &Self) -> subtle::Choice {
37 18 : self.bytes.ct_eq(&other.bytes)
38 18 : }
39 : }
40 :
41 : impl ScramKey {
42 16 : pub(crate) fn sha256(&self) -> Self {
43 16 : Metrics::get().proxy.sha_rounds.inc_by(1);
44 16 : Self {
45 16 : bytes: sha2::Sha256::digest(self.as_bytes()).into(),
46 16 : }
47 16 : }
48 :
49 23 : pub(crate) fn as_bytes(&self) -> [u8; SCRAM_KEY_LEN] {
50 23 : self.bytes
51 23 : }
52 :
53 8 : pub(crate) fn client_key(b: &[u8; 32]) -> Self {
54 : // Prf::new_from_slice will run 2 sha256 rounds.
55 : // Update + Finalize run 2 sha256 rounds.
56 8 : Metrics::get().proxy.sha_rounds.inc_by(4);
57 :
58 8 : let mut prf = Prf::new_from_slice(b).expect("HMAC is able to accept all key sizes");
59 8 : prf.update(b"Client Key");
60 8 : let client_key: [u8; 32] = prf.finalize().into_bytes().into();
61 8 : client_key.into()
62 8 : }
63 : }
64 :
65 : impl From<[u8; SCRAM_KEY_LEN]> for ScramKey {
66 : #[inline(always)]
67 54 : fn from(bytes: [u8; SCRAM_KEY_LEN]) -> Self {
68 54 : Self { bytes }
69 54 : }
70 : }
71 :
72 : impl AsRef<[u8]> for ScramKey {
73 : #[inline(always)]
74 17 : fn as_ref(&self) -> &[u8] {
75 17 : &self.bytes
76 17 : }
77 : }
|