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 | |
| parent | 6bee6eb48c460ccdc638cdb0997946a56ed98c7d (diff) | |
| download | gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.gz gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.bz2 gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.zip | |
[WIP] Rough authentication process done
Diffstat (limited to 'src')
| -rw-r--r-- | src/handlers.rs | 82 | ||||
| -rw-r--r-- | src/schema.rs | 1 | 
2 files changed, 61 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) => { | 
| diff --git a/src/schema.rs b/src/schema.rs index f159d83..fb88640 100644 --- a/src/schema.rs +++ b/src/schema.rs | |||
| @@ -190,6 +190,7 @@ pub struct AuthRequest { | |||
| 190 | #[derive(Serialize, Deserialize, Debug)] | 190 | #[derive(Serialize, Deserialize, Debug)] | 
| 191 | pub struct InitialAuthRequest { | 191 | pub struct InitialAuthRequest { | 
| 192 | pub c: String, | 192 | pub c: String, | 
| 193 | pub iv: [u8; 32], | ||
| 193 | pub key: String, | 194 | pub key: String, | 
| 194 | } | 195 | } | 
| 195 | 196 | ||
