aboutsummaryrefslogtreecommitdiffstats
path: root/src/handlers.rs
diff options
context:
space:
mode:
authorYigit Sever2021-04-14 18:06:06 +0300
committerYigit Sever2021-04-14 19:12:49 +0300
commit71e692ba2a3aa271f7ee1e20d16ac69de70acc5b (patch)
tree2e660d26a3c5445842365951e6eeb5fd4e751434 /src/handlers.rs
parent3e333c952a54453bd877c556a09f2e8e0c434c87 (diff)
downloadgradecoin-71e692ba2a3aa271f7ee1e20d16ac69de70acc5b.tar.gz
gradecoin-71e692ba2a3aa271f7ee1e20d16ac69de70acc5b.tar.bz2
gradecoin-71e692ba2a3aa271f7ee1e20d16ac69de70acc5b.zip
[WIP] Rough authentication process done
Diffstat (limited to 'src/handlers.rs')
-rw-r--r--src/handlers.rs82
1 files changed, 60 insertions, 22 deletions
diff --git a/src/handlers.rs b/src/handlers.rs
index 55d3ab4..21911e1 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -1,12 +1,15 @@
1use base64;
2/// API handlers, the ends of each filter chain 1/// API handlers, the ends of each filter chain
2use aes::Aes128;
3use base64;
3use blake2::{Blake2s, Digest}; 4use blake2::{Blake2s, Digest};
5use block_modes::block_padding::Pkcs7;
6use block_modes::{BlockMode, Cbc};
4use jsonwebtoken::errors::ErrorKind; 7use jsonwebtoken::errors::ErrorKind;
5use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; 8use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation};
6use log::{debug, warn}; 9use log::{debug, warn};
7use md5::Md5; 10use md5::Md5;
8use parking_lot::RwLockUpgradableReadGuard; 11use parking_lot::RwLockUpgradableReadGuard;
9use rsa::{PaddingScheme, RSAPrivateKey}; 12use rsa::{PaddingScheme, RSAPrivateKey, RSAPublicKey};
10use serde::Serialize; 13use serde::Serialize;
11use serde_json; 14use serde_json;
12use sha2; 15use sha2;
@@ -16,6 +19,9 @@ use warp::{http::StatusCode, reply};
16 19
17use crate::PRIVATE_KEY; 20use crate::PRIVATE_KEY;
18 21
22// Encryption primitive
23type Aes128Cbc = Cbc<Aes128, Pkcs7>;
24
19#[derive(Serialize, Debug)] 25#[derive(Serialize, Debug)]
20struct GradeCoinResponse { 26struct GradeCoinResponse {
21 res: ResponseType, 27 res: ResponseType,
@@ -41,21 +47,21 @@ const BEARER: &str = "Bearer ";
41/// the [`AuthRequest.user_id`] of the `request` is not in the list of users that can hold a Gradecoin account 47/// the [`AuthRequest.user_id`] of the `request` is not in the list of users that can hold a Gradecoin account
42/// 48///
43/// # Authentication Process 49/// # Authentication Process
44/// - Gradecoin's Public Key (`G_PK`) is listed on moodle. 50/// - Gradecoin's Public Key (`gradecoin_public_key`) is listed on moodle.
45/// - Gradecoin's Private Key (`G_PR`) is loaded here 51/// - Gradecoin's Private Key (`gradecoin_private_key`) is loaded here
46/// 52///
47/// - Student picks a short temporary key (`k_temp`) 53/// - Student picks a short temporary key (`k_temp`)
48/// - Creates a JSON object (`auth_plaintext`) with their `metu_id` and `public key` in base64 (PEM) format (`S_PK`): 54/// - Creates a JSON object (`auth_plaintext`) with their `metu_id` and `public key` in base64 (PEM) format (`S_PK`):
49/// { 55/// {
50/// student_id: "e12345", 56/// student_id: "e12345",
57/// passwd: "15 char secret"
51/// public_key: "---BEGIN PUBLIC KEY..." 58/// public_key: "---BEGIN PUBLIC KEY..."
52/// } 59/// }
53/// 60///
54/// - Encrypts the serialized string of `auth_plaintext` with AES in TODO format using the temporary key 61/// - 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?
55/// (`k_temp`), the result is `auth_ciphertext`, (TODO base64?) 62/// - The temporary key student has picked `k_temp` is encrypted using RSA with OAEP padding scheme
56/// - The temporary key student has picked `k_temp` is encrypted (TODO details) with `G_PK` (TODO 63/// using sha256 with `gradecoin_public_key` (TODO base64? same as above), giving us `key_ciphertext`
57/// base64?) = `key_ciphertext` 64/// - The payload JSON object (`auth_request`) can be JSON serialized now:
58/// - The payload JSON object (`auth_request`) can be prepared now:
59/// { 65/// {
60/// c: "auth_ciphertext" 66/// c: "auth_ciphertext"
61/// key: "key_ciphertext" 67/// key: "key_ciphertext"
@@ -63,8 +69,10 @@ const BEARER: &str = "Bearer ";
63/// 69///
64/// ## Gradecoin Side 70/// ## Gradecoin Side
65/// 71///
66/// - Upon receiving, we first extract the temporary key by decrypting `key`, receiving `temp_key` 72/// - Upon receiving, we first RSA decrypt with OAEP padding scheme using SHA256 with `gradecoin_private_key` as the key and auth_request.key `key` as the ciphertext, receiving `temp_key` (this is the temporary key chosen by student)
67/// - With this key, we can decrypt c TODO with aes? 73/// - With `temp_key`, we can AES 128 Cbc Pkcs7 decrypt the `auth_request.c`, giving us
74/// auth_plaintext
75/// - The `auth_plaintext` String can be deserialized to [`AuthRequest`]
68/// - We then verify the payload and calculate the User fingerprint 76/// - We then verify the payload and calculate the User fingerprint
69/// - Finally, create the new [`User`] object, insert to users HashMap `<fingerprint, User>` 77/// - Finally, create the new [`User`] object, insert to users HashMap `<fingerprint, User>`
70/// 78///
@@ -74,8 +82,11 @@ pub async fn authenticate_user(
74) -> Result<impl warp::Reply, warp::Rejection> { 82) -> Result<impl warp::Reply, warp::Rejection> {
75 debug!("POST request to /register, authenticate_user"); 83 debug!("POST request to /register, authenticate_user");
76 84
85 // In essence PEM files are just base64 encoded versions of the DER encoded data.
86 // ~tls.mbed.org
87
77 // TODO: lazyload or something <14-04-21, yigit> // 88 // TODO: lazyload or something <14-04-21, yigit> //
78 // This is our key, used to first decrypt the users temporal key 89 // Load our RSA Private Key as DER
79 let der_encoded = PRIVATE_KEY 90 let der_encoded = PRIVATE_KEY
80 .lines() 91 .lines()
81 .filter(|line| !line.starts_with("-")) 92 .filter(|line| !line.starts_with("-"))
@@ -84,23 +95,28 @@ pub async fn authenticate_user(
84 data 95 data
85 }); 96 });
86 97
98 // base64(der(pem))
87 // Our private key is saved in PEM (base64) format 99 // Our private key is saved in PEM (base64) format
88 let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content"); 100 let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content");
89 let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key"); 101 let gradecoin_private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
90 102
91 let padding = PaddingScheme::new_oaep::<sha2::Sha256>(); 103 let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
92 let dec_key = private_key 104 let temp_key = gradecoin_private_key
93 .decrypt(padding, &request.key.as_bytes()) 105 .decrypt(padding, &request.key.as_bytes())
94 .expect("failed to decrypt"); 106 .expect("failed to decrypt");
95 107
96 // then decrypt c using key dec_key 108 // decrypt c using key dec_key
109 let cipher = Aes128Cbc::new_var(&temp_key, &request.iv).unwrap();
110 let auth_plaintext = cipher
111 .decrypt_vec(&base64::decode(request.c).unwrap())
112 .unwrap();
97 113
98 // let request: AuthRequest = serde_json::from_str(&String::from_utf8(dec_data).unwrap()).unwrap(); 114 let request: AuthRequest =
99 let request; 115 serde_json::from_str(&String::from_utf8(auth_plaintext).unwrap()).unwrap();
100 116
101 let provided_id = request.student_id.clone(); 117 let provided_id = request.student_id.clone();
102 118
103 let priv_student_id = match MetuId::new(request.student_id, request.passwd) { 119 let privileged_student_id = match MetuId::new(request.student_id) {
104 Some(id) => id, 120 Some(id) => id,
105 None => { 121 None => {
106 let res_json = warp::reply::json(&GradeCoinResponse { 122 let res_json = warp::reply::json(&GradeCoinResponse {
@@ -117,15 +133,27 @@ pub async fn authenticate_user(
117 if userlist.contains_key(&provided_id) { 133 if userlist.contains_key(&provided_id) {
118 let res_json = warp::reply::json(&GradeCoinResponse { 134 let res_json = warp::reply::json(&GradeCoinResponse {
119 res: ResponseType::Error, 135 res: ResponseType::Error,
120 message: "This user is already authenticated".to_owned(), 136 message:
137 "This user is already authenticated, do you think this is a mistake? Contact me"
138 .to_owned(),
139 });
140
141 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
142 }
143
144 // We're using this as the validator
145 // I hate myself
146 if let Err(_) = DecodingKey::from_rsa_pem(request.public_key.as_bytes()) {
147 let res_json = warp::reply::json(&GradeCoinResponse {
148 res: ResponseType::Error,
149 message: "The supplied RSA public key is not in valid PEM format".to_owned(),
121 }); 150 });
122 151
123 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST)); 152 return Ok(warp::reply::with_status(res_json, StatusCode::BAD_REQUEST));
124 } 153 }
125 154
126 // TODO: audit public key, is it valid? <13-04-21, yigit> //
127 let new_user = User { 155 let new_user = User {
128 user_id: priv_student_id, 156 user_id: privileged_student_id,
129 public_key: request.public_key, 157 public_key: request.public_key,
130 balance: 0, 158 balance: 0,
131 }; 159 };
@@ -146,6 +174,17 @@ pub async fn authenticate_user(
146 Ok(warp::reply::with_status(res_json, StatusCode::CREATED)) 174 Ok(warp::reply::with_status(res_json, StatusCode::CREATED))
147} 175}
148 176
177// fn shed_pem_header_footer(maybe_key: String) -> Result<Vec<u8>, String> {
178// let der_encoded = maybe_key
179// .lines()
180// .filter(|line| !line.starts_with("-"))
181// .fold(String::new(), |mut data, line| {
182// data.push_str(&line);
183// data
184// });
185// Ok(base64::decode(&der_encoded).expect("failed to decode base64 content"))
186// }
187
149/// GET /transaction 188/// GET /transaction
150/// Returns JSON array of transactions 189/// Returns JSON array of transactions
151/// Cannot fail 190/// Cannot fail
@@ -447,7 +486,6 @@ fn authorize_proposer(jwt_token: String, user_pem: &String) -> Result<TokenData<
447 debug!("raw_jwt: {:?}", raw_jwt); 486 debug!("raw_jwt: {:?}", raw_jwt);
448 487
449 // Extract a jsonwebtoken compatible decoding_key from user's public key 488 // Extract a jsonwebtoken compatible decoding_key from user's public key
450 // TODO: just use this for reading users pem key <13-04-21, yigit> //
451 let decoding_key = match DecodingKey::from_rsa_pem(user_pem.as_bytes()) { 489 let decoding_key = match DecodingKey::from_rsa_pem(user_pem.as_bytes()) {
452 Ok(key) => key, 490 Ok(key) => key,
453 Err(j) => { 491 Err(j) => {