diff options
author | Yigit Sever | 2021-04-14 18:06:06 +0300 |
---|---|---|
committer | Yigit Sever | 2021-04-14 19:12:49 +0300 |
commit | 7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf (patch) | |
tree | 5ca6c48fae0f21c1254e6e900514feead091b391 /src/handlers.rs | |
parent | 6bee6eb48c460ccdc638cdb0997946a56ed98c7d (diff) | |
download | gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.gz gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.bz2 gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.zip |
[WIP] Rough authentication process done
Diffstat (limited to 'src/handlers.rs')
-rw-r--r-- | src/handlers.rs | 82 |
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 @@ | |||
1 | use base64; | ||
2 | /// API handlers, the ends of each filter chain | 1 | /// API handlers, the ends of each filter chain |
2 | use aes::Aes128; | ||
3 | use base64; | ||
3 | use blake2::{Blake2s, Digest}; | 4 | use blake2::{Blake2s, Digest}; |
5 | use block_modes::block_padding::Pkcs7; | ||
6 | use block_modes::{BlockMode, Cbc}; | ||
4 | use jsonwebtoken::errors::ErrorKind; | 7 | use jsonwebtoken::errors::ErrorKind; |
5 | use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; | 8 | use jsonwebtoken::{decode, Algorithm, DecodingKey, TokenData, Validation}; |
6 | use log::{debug, warn}; | 9 | use log::{debug, warn}; |
7 | use md5::Md5; | 10 | use md5::Md5; |
8 | use parking_lot::RwLockUpgradableReadGuard; | 11 | use parking_lot::RwLockUpgradableReadGuard; |
9 | use rsa::{PaddingScheme, RSAPrivateKey}; | 12 | use rsa::{PaddingScheme, RSAPrivateKey, RSAPublicKey}; |
10 | use serde::Serialize; | 13 | use serde::Serialize; |
11 | use serde_json; | 14 | use serde_json; |
12 | use sha2; | 15 | use sha2; |
@@ -16,6 +19,9 @@ use warp::{http::StatusCode, reply}; | |||
16 | 19 | ||
17 | use crate::PRIVATE_KEY; | 20 | use crate::PRIVATE_KEY; |
18 | 21 | ||
22 | // Encryption primitive | ||
23 | type Aes128Cbc = Cbc<Aes128, Pkcs7>; | ||
24 | |||
19 | #[derive(Serialize, Debug)] | 25 | #[derive(Serialize, Debug)] |
20 | struct GradeCoinResponse { | 26 | struct 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) => { |