1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use base64;
use ring::digest::{digest, SHA256, SHA256_OUTPUT_LEN};
use ring::hmac::{self, SigningContext, SigningKey};
use ring::pbkdf2;

/// Parses a part of a SCRAM message, after it has been split on commas.
/// Checks to make sure there's a key, and then verifies its the right key.
/// Returns everything after the first '='.
/// Returns a `ExpectedField` error when one of the above conditions fails.
macro_rules! parse_part {
    ($iter:expr, $field:ident, $key:expr) => {
        if let Some(part) = $iter.next() {
            if part.len() < 2 {
                return Err(Error::Protocol(Kind::ExpectedField(Field::$field)));
            } else if &part.as_bytes()[..2] == $key {
                &part[2..]
            } else {
                return Err(Error::Protocol(Kind::ExpectedField(Field::$field)));
            }
        } else {
            return Err(Error::Protocol(Kind::ExpectedField(Field::$field)));
        }
    };
}

/// Hashes a password with SHA-256 with the given salt and number of iterations.  This should
/// be used by [`AuthenticationProvider`](server/trait.AuthenticationProvider.html) implementors
/// to hash any passwords prior to being saved.
pub fn hash_password(password: &str, iterations: u16, salt: &[u8]) -> [u8; SHA256_OUTPUT_LEN] {
    let mut salted_password = [0u8; SHA256_OUTPUT_LEN];
    pbkdf2::derive(
        &SHA256,
        u32::from(iterations),
        salt,
        password.as_bytes(),
        &mut salted_password,
    );
    salted_password
}

/// Finds the client proof and server signature based on the shared hashed key.
pub fn find_proofs(
    gs2header: &str,
    client_first_bare: &str,
    server_first: &str,
    salted_password: &[u8],
    nonce: &str,
) -> ([u8; SHA256_OUTPUT_LEN], hmac::Signature) {
    fn sign_slice(key: &SigningKey, slice: &[&[u8]]) -> hmac::Signature {
        let mut signature_context = SigningContext::with_key(key);
        for item in slice {
            signature_context.update(item);
        }
        signature_context.sign()
    }

    let client_final_without_proof =
        format!("c={},r={}", base64::encode(gs2header.as_bytes()), nonce);
    let auth_message = [
        client_first_bare.as_bytes(),
        b",",
        server_first.as_bytes(),
        b",",
        client_final_without_proof.as_bytes(),
    ];

    let salted_password_signing_key = SigningKey::new(&SHA256, salted_password);
    let client_key = hmac::sign(&salted_password_signing_key, b"Client Key");
    let server_key = hmac::sign(&salted_password_signing_key, b"Server Key");
    let stored_key = digest(&SHA256, client_key.as_ref());
    let stored_key_signing_key = SigningKey::new(&SHA256, stored_key.as_ref());
    let client_signature = sign_slice(&stored_key_signing_key, &auth_message);
    let server_signature_signing_key = SigningKey::new(&SHA256, server_key.as_ref());
    let server_signature = sign_slice(&server_signature_signing_key, &auth_message);
    let mut client_proof = [0u8; SHA256_OUTPUT_LEN];
    let xor_iter = client_key
        .as_ref()
        .iter()
        .zip(client_signature.as_ref())
        .map(|(k, s)| k ^ s);
    for (p, x) in client_proof.iter_mut().zip(xor_iter) {
        *p = x
    }
    (client_proof, server_signature)
}