aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYigit Sever2021-04-14 18:06:06 +0300
committerYigit Sever2021-04-14 19:12:49 +0300
commit7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf (patch)
tree5ca6c48fae0f21c1254e6e900514feead091b391
parent6bee6eb48c460ccdc638cdb0997946a56ed98c7d (diff)
downloadgradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.gz
gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.tar.bz2
gradecoin-7712bcd30ec6da938a12352bcec6bbd0bfcb0dcf.zip
[WIP] Rough authentication process done
-rw-r--r--Cargo.lock58
-rw-r--r--Cargo.toml2
-rw-r--r--src/handlers.rs82
-rw-r--r--src/schema.rs1
4 files changed, 121 insertions, 22 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 03c6eb0..fa69ae2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,37 @@
1# This file is automatically @generated by Cargo. 1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing. 2# It is not intended for manual editing.
3[[package]] 3[[package]]
4name = "aes"
5version = "0.6.0"
6source = "registry+https://github.com/rust-lang/crates.io-index"
7checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
8dependencies = [
9 "aes-soft",
10 "aesni",
11 "cipher",
12]
13
14[[package]]
15name = "aes-soft"
16version = "0.6.4"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
19dependencies = [
20 "cipher",
21 "opaque-debug",
22]
23
24[[package]]
25name = "aesni"
26version = "0.10.0"
27source = "registry+https://github.com/rust-lang/crates.io-index"
28checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
29dependencies = [
30 "cipher",
31 "opaque-debug",
32]
33
34[[package]]
4name = "aho-corasick" 35name = "aho-corasick"
5version = "0.7.15" 36version = "0.7.15"
6source = "registry+https://github.com/rust-lang/crates.io-index" 37source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -71,6 +102,22 @@ dependencies = [
71] 102]
72 103
73[[package]] 104[[package]]
105name = "block-modes"
106version = "0.7.0"
107source = "registry+https://github.com/rust-lang/crates.io-index"
108checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0"
109dependencies = [
110 "block-padding",
111 "cipher",
112]
113
114[[package]]
115name = "block-padding"
116version = "0.2.1"
117source = "registry+https://github.com/rust-lang/crates.io-index"
118checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
119
120[[package]]
74name = "buf_redux" 121name = "buf_redux"
75version = "0.8.4" 122version = "0.8.4"
76source = "registry+https://github.com/rust-lang/crates.io-index" 123source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -137,6 +184,15 @@ dependencies = [
137] 184]
138 185
139[[package]] 186[[package]]
187name = "cipher"
188version = "0.2.5"
189source = "registry+https://github.com/rust-lang/crates.io-index"
190checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
191dependencies = [
192 "generic-array",
193]
194
195[[package]]
140name = "cloudabi" 196name = "cloudabi"
141version = "0.0.3" 197version = "0.0.3"
142source = "registry+https://github.com/rust-lang/crates.io-index" 198source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -319,8 +375,10 @@ dependencies = [
319name = "gradecoin" 375name = "gradecoin"
320version = "0.1.0" 376version = "0.1.0"
321dependencies = [ 377dependencies = [
378 "aes",
322 "base64 0.13.0", 379 "base64 0.13.0",
323 "blake2", 380 "blake2",
381 "block-modes",
324 "chrono", 382 "chrono",
325 "hex-literal", 383 "hex-literal",
326 "jsonwebtoken", 384 "jsonwebtoken",
diff --git a/Cargo.toml b/Cargo.toml
index 3b6e71e..b5733b2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,6 +23,8 @@ md-5 = "0.9.1"
23rsa = "0.4.0" 23rsa = "0.4.0"
24base64 = "0.13.0" 24base64 = "0.13.0"
25sha2 = "0.9.3" 25sha2 = "0.9.3"
26block-modes = "0.7.0"
27aes = "0.6.0"
26 28
27[dev-dependencies] 29[dev-dependencies]
28serde_test = "1.0.117" 30serde_test = "1.0.117"
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) => {
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)]
191pub struct InitialAuthRequest { 191pub 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