aboutsummaryrefslogtreecommitdiffstats
path: root/src/handlers.rs
diff options
context:
space:
mode:
authoralpaylan2021-04-16 01:03:21 +0300
committeralpaylan2021-04-16 01:03:21 +0300
commitd248309f8595701a0fddd2462b963bcad55f18c8 (patch)
tree109d4e2809f9f3392612e86ab3d5a47df5830b11 /src/handlers.rs
parent711d987b8e060682cf2215f25392415e206b3e8d (diff)
parenta1af17aad7c1308fc714a60595bae07cc8bb8a9a (diff)
downloadgradecoin-d248309f8595701a0fddd2462b963bcad55f18c8.tar.gz
gradecoin-d248309f8595701a0fddd2462b963bcad55f18c8.tar.bz2
gradecoin-d248309f8595701a0fddd2462b963bcad55f18c8.zip
Merge remote-tracking branch 'origin/main'
# Conflicts: # src/schema.rs
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs223
1 files changed, 167 insertions, 56 deletions
diff --git a/src/handlers.rs b/src/handlers.rs
index a8c9947..fe60ded 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -1,6 +1,6 @@
1/// API handlers, the ends of each filter chain
2use aes::Aes128; 1use aes::Aes128;
3use base64; 2/// API handlers, the ends of each filter chain
3use askama::Template;
4use blake2::{Blake2s, Digest}; 4use blake2::{Blake2s, Digest};
5use block_modes::block_padding::Pkcs7; 5use block_modes::block_padding::Pkcs7;
6use block_modes::{BlockMode, Cbc}; 6use block_modes::{BlockMode, Cbc};
@@ -8,10 +8,8 @@ use jsonwebtoken::errors::ErrorKind;
8use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; 8use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation};
9use log::{debug, warn}; 9use log::{debug, warn};
10use md5::Md5; 10use md5::Md5;
11use parking_lot::RwLockUpgradableReadGuard;
12use rsa::{PaddingScheme, RSAPrivateKey}; 11use rsa::{PaddingScheme, RSAPrivateKey};
13use serde::Serialize; 12use serde::Serialize;
14use serde_json;
15use sha2::Sha256; 13use sha2::Sha256;
16use std::collections::HashMap; 14use std::collections::HashMap;
17use std::convert::Infallible; 15use std::convert::Infallible;
@@ -39,6 +37,7 @@ enum ResponseType {
39 37
40use crate::schema::{ 38use crate::schema::{
41 AuthRequest, Block, Claims, Db, InitialAuthRequest, MetuId, NakedBlock, Transaction, User, 39 AuthRequest, Block, Claims, Db, InitialAuthRequest, MetuId, NakedBlock, Transaction, User,
40 UserAtRest,
42}; 41};
43 42
44const BEARER: &str = "Bearer "; 43const BEARER: &str = "Bearer ";
@@ -61,9 +60,9 @@ const BEARER: &str = "Bearer ";
61/// public_key: "---BEGIN PUBLIC KEY..." 60/// public_key: "---BEGIN PUBLIC KEY..."
62/// } 61/// }
63/// 62///
64/// - Encrypts the serialized string of `auth_plaintext` with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (`k_temp`), the result is `auth_ciphertext` TODO should this be base64'd? 63/// - Encrypts the serialized string of `auth_plaintext` with 128 bit block AES in CBC mode with Pkcs7 padding using the temporary key (`k_temp`), the result is `auth_ciphertext`
65/// - The temporary key student has picked `k_temp` is encrypted using RSA with OAEP padding scheme 64/// - The temporary key student has picked `k_temp` is encrypted using RSA with OAEP padding scheme
66/// using sha256 with `gradecoin_public_key` (TODO base64? same as above), giving us `key_ciphertext` 65/// using sha256 with `gradecoin_public_key`, giving us `key_ciphertext`
67/// - The payload JSON object (`auth_request`) can be JSON serialized now: 66/// - The payload JSON object (`auth_request`) can be JSON serialized now:
68/// { 67/// {
69/// c: "auth_ciphertext" 68/// c: "auth_ciphertext"
@@ -92,7 +91,7 @@ pub async fn authenticate_user(
92 // Load our RSA Private Key as DER 91 // Load our RSA Private Key as DER
93 let der_encoded = PRIVATE_KEY 92 let der_encoded = PRIVATE_KEY
94 .lines() 93 .lines()
95 .filter(|line| !line.starts_with("-")) 94 .filter(|line| !line.starts_with('-'))
96 .fold(String::new(), |mut data, line| { 95 .fold(String::new(), |mut data, line| {
97 data.push_str(&line); 96 data.push_str(&line);
98 data 97 data
@@ -104,18 +103,126 @@ pub async fn authenticate_user(
104 let gradecoin_private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key"); 103 let gradecoin_private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
105 104
106 let padding = PaddingScheme::new_oaep::<sha2::Sha256>(); 105 let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
107 let temp_key = gradecoin_private_key
108 .decrypt(padding, &request.key.as_bytes())
109 .expect("failed to decrypt");
110 106
111 // decrypt c using key dec_key 107 let key_ciphertext = match base64::decode(&request.key) {
112 let cipher = Aes128Cbc::new_var(&temp_key, &request.iv).unwrap(); 108 Ok(c) => c,
113 let auth_plaintext = cipher 109 Err(err) => {
114 .decrypt_vec(&base64::decode(request.c).unwrap()) 110 debug!(
115 .unwrap(); 111 "The ciphertext of the key was not base64 encoded {}, {}",
112 &request.key, err
113 );
114
115 let res_json = warp::reply::json(&GradeCoinResponse {
116 res: ResponseType::Error,
117 message: "The ciphertext of the key was not base64 encoded {}, {}".to_owned(),
118 });
119
120 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
121 }
122 };
123
124 let temp_key = match gradecoin_private_key.decrypt(padding, &key_ciphertext) {
125 Ok(k) => k,
126 Err(err) => {
127 debug!(
128 "Failed to decrypt ciphertext {:?}, {}",
129 &key_ciphertext, err
130 );
131
132 let res_json = warp::reply::json(&GradeCoinResponse {
133 res: ResponseType::Error,
134 message: "Failed to decrypt the ciphertext of the temporary key".to_owned(),
135 });
136
137 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
138 }
139 };
140
141 let cipher = match Aes128Cbc::new_var(&temp_key, &request.iv.as_bytes()) {
142 Ok(c) => c,
143 Err(err) => {
144 debug!(
145 "Could not create a cipher from temp_key and request.iv {:?}, {}, {}",
146 &temp_key, &request.iv, err
147 );
148
149 let res_json = warp::reply::json(&GradeCoinResponse {
150 res: ResponseType::Error,
151 message: "Given IV has invalid length".to_owned(),
152 });
153
154 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
155 }
156 };
116 157
117 let request: AuthRequest = 158 let auth_packet = match base64::decode(&request.c) {
118 serde_json::from_str(&String::from_utf8(auth_plaintext).unwrap()).unwrap(); 159 Ok(a) => a,
160
161 Err(err) => {
162 debug!(
163 "The auth_packet (c field) did not base64 decode {} {}",
164 &request.c, err
165 );
166
167 let res_json = warp::reply::json(&GradeCoinResponse {
168 res: ResponseType::Error,
169 message: "The c field was not correctly base64 encoded".to_owned(),
170 });
171
172 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
173 }
174 };
175
176 let auth_plaintext = match cipher.decrypt_vec(&auth_packet) {
177 Ok(p) => p,
178 Err(err) => {
179 debug!(
180 "Base64 decoded auth request did not decrypt correctly {:?} {}",
181 &auth_packet, err
182 );
183
184 let res_json = warp::reply::json(&GradeCoinResponse {
185 res: ResponseType::Error,
186 message: "The Bas64 decoded auth request did not decrypt correctly".to_owned(),
187 });
188
189 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
190 }
191 };
192
193 let utf8_auth_plaintext = match String::from_utf8(auth_plaintext.clone()) {
194 Ok(text) => text,
195 Err(err) => {
196 debug!(
197 "Auth plaintext did not convert into utf8 {:?} {}",
198 &auth_plaintext, err
199 );
200
201 let res_json = warp::reply::json(&GradeCoinResponse {
202 res: ResponseType::Error,
203 message: "Auth plaintext couldn't get converted to UTF-8".to_owned(),
204 });
205
206 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
207 }
208 };
209
210 let request: AuthRequest = match serde_json::from_str(&utf8_auth_plaintext) {
211 Ok(req) => req,
212 Err(err) => {
213 debug!(
214 "Auth plaintext did not serialize correctly {:?} {}",
215 &utf8_auth_plaintext, err
216 );
217
218 let res_json = warp::reply::json(&GradeCoinResponse {
219 res: ResponseType::Error,
220 message: "The auth request JSON did not serialize correctly".to_owned(),
221 });
222
223 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
224 }
225 };
119 226
120 let provided_id = request.student_id.clone(); 227 let provided_id = request.student_id.clone();
121 228
@@ -131,33 +238,24 @@ pub async fn authenticate_user(
131 } 238 }
132 }; 239 };
133 240
134 let userlist = db.users.upgradable_read(); 241 {
135 242 let userlist = db.users.read();
136 if userlist.contains_key(&provided_id) {
137 let res_json = warp::reply::json(&GradeCoinResponse {
138 res: ResponseType::Error,
139 message:
140 "This user is already authenticated, do you think this is a mistake? Contact me"
141 .to_owned(),
142 });
143
144 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
145 }
146 243
147 // We're using this as the validator 244 if userlist.contains_key(&provided_id) {
148 // I hate myself 245 let res_json = warp::reply::json(&GradeCoinResponse {
149 if let Err(_) = DecodingKey::from_rsa_pem(request.public_key.as_bytes()) { 246 res: ResponseType::Error,
150 let res_json = warp::reply::json(&GradeCoinResponse { 247 message:
151 res: ResponseType::Error, 248 "This user is already authenticated, do you think this is a mistake? Contact me"
152 message: "The supplied RSA public key is not in valid PEM format".to_owned(), 249 .to_owned(),
153 }); 250 });
154 251
155 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); 252 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
253 }
156 } 254 }
157 255
158 // We're using this as the validator 256 // We're using this as the validator
159 // I hate myself 257 // I hate myself
160 if let Err(_) = DecodingKey::from_rsa_pem(request.public_key.as_bytes()) { 258 if DecodingKey::from_rsa_pem(request.public_key.as_bytes()).is_err() {
161 let res_json = warp::reply::json(&GradeCoinResponse { 259 let res_json = warp::reply::json(&GradeCoinResponse {
162 res: ResponseType::Error, 260 res: ResponseType::Error,
163 message: "The supplied RSA public key is not in valid PEM format".to_owned(), 261 message: "The supplied RSA public key is not in valid PEM format".to_owned(),
@@ -174,11 +272,19 @@ pub async fn authenticate_user(
174 balance: 0, 272 balance: 0,
175 }; 273 };
176 274
177 let user_json = serde_json::to_string(&new_user).unwrap(); 275 let user_at_rest_json = serde_json::to_string(&UserAtRest {
276 user: User {
277 user_id: new_user.user_id.clone(),
278 public_key: new_user.public_key.clone(),
279 balance: 0,
280 },
281 fingerprint: fingerprint.clone(),
282 })
283 .unwrap();
178 284
179 fs::write(format!("users/{}.guy", new_user.user_id), user_json).unwrap(); 285 fs::write(format!("users/{}.guy", new_user.user_id), user_at_rest_json).unwrap();
180 286
181 let mut userlist = RwLockUpgradableReadGuard::upgrade(userlist); 287 let mut userlist = db.users.write();
182 288
183 userlist.insert(fingerprint.clone(), new_user); 289 userlist.insert(fingerprint.clone(), new_user);
184 290
@@ -193,17 +299,6 @@ pub async fn authenticate_user(
193 Ok(warp::reply::with_status(res_json, StatusCode::CREATED)) 299 Ok(warp::reply::with_status(res_json, StatusCode::CREATED))
194} 300}
195 301
196// fn shed_pem_header_footer(maybe_key: String) -> Result<Vec<u8>, String> {
197// let der_encoded = maybe_key
198// .lines()
199// .filter(|line| !line.starts_with("-"))
200// .fold(String::new(), |mut data, line| {
201// data.push_str(&line);
202// data
203// });
204// Ok(base64::decode(&der_encoded).expect("failed to decode base64 content"))
205// }
206
207/// GET /transaction 302/// GET /transaction
208/// Returns JSON array of transactions 303/// Returns JSON array of transactions
209/// Cannot fail 304/// Cannot fail
@@ -241,7 +336,7 @@ pub async fn authorized_propose_block(
241 336
242 println!("{:?}", &new_block); 337 println!("{:?}", &new_block);
243 338
244 if new_block.transaction_list.len() < 1 { 339 if new_block.transaction_list.is_empty() {
245 let res_json = warp::reply::json(&GradeCoinResponse { 340 let res_json = warp::reply::json(&GradeCoinResponse {
246 res: ResponseType::Error, 341 res: ResponseType::Error,
247 message: format!( 342 message: format!(
@@ -322,8 +417,8 @@ pub async fn authorized_propose_block(
322 417
323 let naked_block = NakedBlock { 418 let naked_block = NakedBlock {
324 transaction_list: new_block.transaction_list.clone(), 419 transaction_list: new_block.transaction_list.clone(),
325 nonce: new_block.nonce.clone(), 420 nonce: new_block.nonce,
326 timestamp: new_block.timestamp.clone(), 421 timestamp: new_block.timestamp,
327 }; 422 };
328 423
329 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap(); 424 let naked_block_flat = serde_json::to_vec(&naked_block).unwrap();
@@ -556,7 +651,7 @@ pub async fn list_blocks(db: Db) -> Result<impl warp::Reply, Infallible> {
556/// *[`jwt_token`]: The raw JWT token, "Bearer aaa.bbb.ccc" 651/// *[`jwt_token`]: The raw JWT token, "Bearer aaa.bbb.ccc"
557/// *[`user_pem`]: User Public Key, "BEGIN RSA" 652/// *[`user_pem`]: User Public Key, "BEGIN RSA"
558/// NOT async, might look into it if this becomes a bottleneck 653/// NOT async, might look into it if this becomes a bottleneck
559fn authorize_proposer(jwt_token: String, user_pem: &String) -> Result<TokenData<Claims>, String> { 654fn authorize_proposer(jwt_token: String, user_pem: &str) -> Result<TokenData<Claims>, String> {
560 // Throw away the "Bearer " part 655 // Throw away the "Bearer " part
561 let raw_jwt = jwt_token.trim_start_matches(BEARER).to_owned(); 656 let raw_jwt = jwt_token.trim_start_matches(BEARER).to_owned();
562 debug!("raw_jwt: {:?}", raw_jwt); 657 debug!("raw_jwt: {:?}", raw_jwt);
@@ -599,3 +694,19 @@ fn authorize_proposer(jwt_token: String, user_pem: &String) -> Result<TokenData<
599 694
600 Ok(token_payload) 695 Ok(token_payload)
601} 696}
697
698#[derive(Template)]
699#[template(path = "welcome.html")]
700struct WelcomeTemplate<'a> {
701 title: &'a str,
702 body: &'a str,
703}
704
705pub async fn welcome_handler() -> Result<impl warp::Reply, warp::Rejection> {
706 let template = WelcomeTemplate {
707 title: "Welcome",
708 body: "To The Bookstore!",
709 };
710 let res = template.render().unwrap();
711 Ok(warp::reply::html(res))
712}