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 : }
|