1use crate::{
2 error::NcryptfError as Error, signature::Signature, token::Token, util::randombytes_buf,
3};
4
5use chrono::{offset::Utc, DateTime, Timelike};
6use hkdf::Hkdf;
7use hmac::{Hmac, Mac};
8use serde::{Deserialize, Serialize};
9use sha2::Sha256;
10use base64::{Engine as _, engine::general_purpose};
11
12const AUTH_INFO: &str = "HMAC|AuthenticationKey";
14
15#[derive(Debug, Deserialize)]
17struct AuthParamsJson {
18 pub access_token: String,
19 pub hmac: String,
20 pub salt: String,
21 pub date: String,
22}
23
24#[derive(Debug)]
26pub struct AuthParams {
27 pub access_token: String,
28 pub hmac: Vec<u8>,
29 pub salt: Vec<u8>,
30 pub version: Option<i8>,
31 pub date: Option<DateTime<Utc>>,
32}
33
34#[derive(Debug)]
36pub struct Authorization {
37 pub token: Token,
38 pub salt: Vec<u8>,
39 pub date: DateTime<Utc>,
40 pub signature: String,
41 pub hmac: Vec<u8>,
42 pub version: Option<i8>,
43}
44
45impl Authorization {
46 pub fn from(
48 method: String,
49 uri: String,
50 token: Token,
51 date: DateTime<Utc>,
52 payload: String,
53 salt: Option<Vec<u8>>,
54 version: Option<i8>,
55 ) -> Result<Self, Error> {
56 let m = method.to_uppercase();
57 let s = match salt {
58 Some(s) => s,
59 None => randombytes_buf(32),
60 };
61
62 let v = match version {
63 Some(v) => Some(v),
64 None => Some(crate::NCRYPTF_CURRENT_VERSION),
65 };
66
67 let sr: &[u8; 32] = &s.clone().try_into().unwrap();
68 let ikm: &[u8; 32] = &token.clone().ikm.try_into().unwrap();
69 let signature = Signature::derive(m, uri, s.clone(), date, payload, v);
70
71 let hkdf = Hkdf::<Sha256>::new(Some(sr), ikm);
72 let mut okm = [0u8; 32];
73 match hkdf.expand(&AUTH_INFO.as_bytes(), &mut okm) {
74 Err(_) => {
75 return Err(Error::InvalidArgument(format!(
76 "Unable to generate HMAC for token."
77 )));
78 }
79 Ok(_) => {}
80 };
81
82 let hk = okm.to_vec();
83 let hkdf_string = hex::encode(hk.clone()).to_string().to_lowercase();
84
85 let mut hmac = Hmac::<Sha256>::new_from_slice(hkdf_string.as_bytes())
86 .expect("HMAC can take key of any size");
87 hmac.update(signature.as_bytes());
88 let result = hmac.finalize();
89 let bytes = result.into_bytes();
90 let hmac = bytes.to_vec();
91 return Ok(Authorization {
92 token,
93 salt: s.clone(),
94 date,
95 signature,
96 hmac: hmac,
97 version: v,
98 });
99 }
100
101 pub fn get_date(&self) -> DateTime<Utc> {
103 return self.date;
104 }
105
106 pub fn get_date_string(&self) -> String {
108 return self.date.format("%a, %d %b %Y %H:%M:%S %z").to_string();
109 }
110
111 pub fn get_hmac(&self) -> Vec<u8> {
113 return self.hmac.clone();
114 }
115
116 pub fn get_encoded_hmac(&self) -> String {
118 let hmac = self.get_hmac();
119 return general_purpose::STANDARD.encode(hmac);
120 }
121
122 pub fn get_encoded_salt(&self) -> String {
124 let salt = self.salt.clone();
125 return general_purpose::STANDARD.encode(salt);
126 }
127
128 pub fn get_signature_string(&self) -> String {
130 return self.signature.clone();
131 }
132
133 pub fn get_time_drift(date: DateTime<Utc>) -> i32 {
135 let now = Utc::now();
136 return now.second().abs_diff(date.second()).try_into().unwrap();
137 }
138
139 pub fn verify(&self, hmac: Vec<u8>, drift_allowance: i32) -> bool {
142 let drift = Self::get_time_drift(self.get_date());
143 if drift >= drift_allowance {
144 return false;
145 }
146
147 if constant_time_eq::constant_time_eq(&hmac, &self.get_hmac()) {
148 return true;
149 }
150
151 return false;
152 }
153
154 pub fn get_header(&self) -> String {
156 let salt = self.get_encoded_salt();
157 let hmac = self.get_encoded_hmac();
158
159 match self.version {
160 Some(2) => {
161 let d = AuthStruct {
162 access_token: self.token.access_token.clone(),
163 date: self.get_date_string(),
164 hmac: hmac.clone(),
165 salt: salt.clone(),
166 v: 2,
167 };
168
169 let json = serde_json::to_string(&d)
171 .unwrap()
172 .to_string()
173 .replace("/", "\\/");
174 return format!("HMAC {}", general_purpose::STANDARD.encode(json));
175 }
176 _ => {
177 return format!("HMAC {},{},{}", self.token.access_token, hmac, salt);
178 }
179 };
180 }
181
182 pub fn extract_params_from_header_string(header: String) -> Result<AuthParams, Error> {
184 if header.starts_with("HMAC ") {
185 let auth_header = header.replace("HMAC ", "");
186 if auth_header.contains(",") {
187 let params: Vec<String> = auth_header.split(",").map(|s| s.to_string()).collect();
188 if params.len() != 3 {
189 return Err(Error::InvalidArgument(String::from(
190 "Header parameters are not valid.",
191 )));
192 }
193
194 return Ok(AuthParams {
195 access_token: params[0].clone(),
196 hmac: general_purpose::STANDARD.decode(params[1].clone()).unwrap(),
197 salt: general_purpose::STANDARD.decode(params[2].clone()).unwrap(),
198 version: Some(1),
199 date: None,
200 });
201 } else {
202 let json = general_purpose::STANDARD.decode(auth_header).unwrap();
203 match serde_json::from_str::<AuthParamsJson>(
204 String::from_utf8(json).unwrap().as_str(),
205 ) {
206 Ok(params) => {
207 let date = chrono::DateTime::parse_from_rfc2822(¶ms.date);
208 if date.is_ok() {
209 let d = date.unwrap().with_timezone(&Utc);
210 return Ok(AuthParams {
211 access_token: params.access_token,
212 hmac: general_purpose::STANDARD.decode(params.hmac).unwrap(),
213 salt: general_purpose::STANDARD.decode(params.salt).unwrap(),
214 version: Some(2),
215 date: Some(d),
216 });
217 }
218 }
219 _ => {}
220 };
221 }
222 }
223
224 return Err(Error::InvalidArgument(String::from(
225 "Header parameters are not valid.",
226 )));
227 }
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232struct AuthStruct {
233 pub access_token: String,
234 pub date: String,
235 pub hmac: String,
236 pub salt: String,
237 pub v: i8,
238}