-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathutils.rs
More file actions
91 lines (86 loc) · 3.29 KB
/
utils.rs
File metadata and controls
91 lines (86 loc) · 3.29 KB
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
86
87
88
89
90
91
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine;
use ring::digest::{self, digest, SHA256_OUTPUT_LEN};
use ring::hmac::{self, Context, Key, HMAC_SHA256};
use ring::pbkdf2::{self, PBKDF2_HMAC_SHA256 as SHA256};
use std::num::NonZeroU32;
/// 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`](crate::server::AuthenticationProvider) implementors to
/// hash any passwords prior to being saved.
pub fn hash_password(
password: &str,
iterations: NonZeroU32,
salt: &[u8],
) -> [u8; SHA256_OUTPUT_LEN] {
let mut salted_password = [0u8; SHA256_OUTPUT_LEN];
pbkdf2::derive(
SHA256,
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::Tag) {
fn sign_slice(key: &Key, slice: &[&[u8]]) -> hmac::Tag {
let mut signature_context = Context::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 = Key::new(HMAC_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(&digest::SHA256, client_key.as_ref());
let stored_key_signing_key = Key::new(HMAC_SHA256, stored_key.as_ref());
let client_signature = sign_slice(&stored_key_signing_key, &auth_message);
let server_signature_signing_key = Key::new(HMAC_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)
}