mirror of
				https://github.com/dani-garcia/vaultwarden.git
				synced 2025-10-27 16:30:02 +02:00 
			
		
		
		
	Load RSA keys as pem format directly, and using openssl crate, backported from async branch
This commit is contained in:
		| @@ -27,7 +27,6 @@ pub fn routes() -> Vec<Route> { | ||||
| // | ||||
| // Move this somewhere else | ||||
| // | ||||
| use rocket::response::Response; | ||||
| use rocket::Route; | ||||
| use rocket_contrib::json::Json; | ||||
| use serde_json::Value; | ||||
| @@ -41,7 +40,7 @@ use crate::{ | ||||
| }; | ||||
|  | ||||
| #[put("/devices/identifier/<uuid>/clear-token")] | ||||
| fn clear_device_token<'a>(uuid: String) -> Response<'a> { | ||||
| fn clear_device_token<'a>(uuid: String) -> &'static str { | ||||
|     // This endpoint doesn't have auth header | ||||
|  | ||||
|     let _ = uuid; | ||||
| @@ -50,7 +49,7 @@ fn clear_device_token<'a>(uuid: String) -> Response<'a> { | ||||
|     // This only clears push token | ||||
|     // https://github.com/bitwarden/core/blob/master/src/Api/Controllers/DevicesController.cs#L109 | ||||
|     // https://github.com/bitwarden/core/blob/master/src/Core/Services/Implementations/DeviceService.cs#L37 | ||||
|     Response::new() | ||||
|     "" | ||||
| } | ||||
|  | ||||
| #[put("/devices/identifier/<uuid>/token", data = "<data>")] | ||||
|   | ||||
| @@ -343,7 +343,7 @@ fn parse_duo_values(key: &str, val: &str, ikey: &str, prefix: &str, time: i64) - | ||||
|         err!("Invalid ikey") | ||||
|     } | ||||
|  | ||||
|     let expire = match expire.parse() { | ||||
|     let expire: i64 = match expire.parse() { | ||||
|         Ok(e) => e, | ||||
|         Err(_) => err!("Invalid expire time"), | ||||
|     }; | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/auth.rs
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/auth.rs
									
									
									
									
									
								
							| @@ -27,17 +27,26 @@ static JWT_VERIFYEMAIL_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|verifyema | ||||
| static JWT_ADMIN_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|admin", CONFIG.domain_origin())); | ||||
| static JWT_SEND_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|send", CONFIG.domain_origin())); | ||||
|  | ||||
| static PRIVATE_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.private_rsa_key()) { | ||||
|     Ok(key) => key, | ||||
|     Err(e) => panic!("Error loading private RSA Key.\n Error: {}", e), | ||||
| static PRIVATE_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| { | ||||
|     read_file(&CONFIG.private_rsa_key()).unwrap_or_else(|e| panic!("Error loading private RSA Key.\n{}", e)) | ||||
| }); | ||||
| static PUBLIC_RSA_KEY: Lazy<Vec<u8>> = Lazy::new(|| match read_file(&CONFIG.public_rsa_key()) { | ||||
|     Ok(key) => key, | ||||
|     Err(e) => panic!("Error loading public RSA Key.\n Error: {}", e), | ||||
| static PRIVATE_RSA_KEY: Lazy<EncodingKey> = Lazy::new(|| { | ||||
|     EncodingKey::from_rsa_pem(&PRIVATE_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding private RSA Key.\n{}", e)) | ||||
| }); | ||||
| static PUBLIC_RSA_KEY_VEC: Lazy<Vec<u8>> = Lazy::new(|| { | ||||
|     read_file(&CONFIG.public_rsa_key()).unwrap_or_else(|e| panic!("Error loading public RSA Key.\n{}", e)) | ||||
| }); | ||||
| static PUBLIC_RSA_KEY: Lazy<DecodingKey> = Lazy::new(|| { | ||||
|     DecodingKey::from_rsa_pem(&PUBLIC_RSA_KEY_VEC).unwrap_or_else(|e| panic!("Error decoding public RSA Key.\n{}", e)) | ||||
| }); | ||||
|  | ||||
| pub fn load_keys() { | ||||
|     Lazy::force(&PRIVATE_RSA_KEY); | ||||
|     Lazy::force(&PUBLIC_RSA_KEY); | ||||
| } | ||||
|  | ||||
| pub fn encode_jwt<T: Serialize>(claims: &T) -> String { | ||||
|     match jsonwebtoken::encode(&JWT_HEADER, claims, &EncodingKey::from_rsa_der(&PRIVATE_RSA_KEY)) { | ||||
|     match jsonwebtoken::encode(&JWT_HEADER, claims, &PRIVATE_RSA_KEY) { | ||||
|         Ok(token) => token, | ||||
|         Err(e) => panic!("Error encoding jwt {}", e), | ||||
|     } | ||||
| @@ -55,10 +64,7 @@ fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Err | ||||
|     }; | ||||
|  | ||||
|     let token = token.replace(char::is_whitespace, ""); | ||||
|  | ||||
|     jsonwebtoken::decode(&token, &DecodingKey::from_rsa_der(&PUBLIC_RSA_KEY), &validation) | ||||
|         .map(|d| d.claims) | ||||
|         .map_res("Error decoding JWT") | ||||
|     jsonwebtoken::decode(&token, &&PUBLIC_RSA_KEY, &validation).map(|d| d.claims).map_res("Error decoding JWT") | ||||
| } | ||||
|  | ||||
| pub fn decode_login(token: &str) -> Result<LoginJwtClaims, Error> { | ||||
|   | ||||
| @@ -770,13 +770,10 @@ impl Config { | ||||
|     } | ||||
|  | ||||
|     pub fn private_rsa_key(&self) -> String { | ||||
|         format!("{}.der", CONFIG.rsa_key_filename()) | ||||
|     } | ||||
|     pub fn private_rsa_key_pem(&self) -> String { | ||||
|         format!("{}.pem", CONFIG.rsa_key_filename()) | ||||
|     } | ||||
|     pub fn public_rsa_key(&self) -> String { | ||||
|         format!("{}.pub.der", CONFIG.rsa_key_filename()) | ||||
|         format!("{}.pub.pem", CONFIG.rsa_key_filename()) | ||||
|     } | ||||
|     pub fn mail_enabled(&self) -> bool { | ||||
|         let inner = &self.inner.read().unwrap().config; | ||||
|   | ||||
| @@ -50,6 +50,7 @@ use std::time::SystemTimeError as TimeErr; | ||||
| use u2f::u2ferror::U2fError as U2fErr; | ||||
| use webauthn_rs::error::WebauthnError as WebauthnErr; | ||||
| use yubico::yubicoerror::YubicoError as YubiErr; | ||||
| use openssl::error::ErrorStack as SSLErr; | ||||
|  | ||||
| #[derive(Serialize)] | ||||
| pub struct Empty {} | ||||
| @@ -82,6 +83,7 @@ make_error! { | ||||
|     Lettre(LettreErr): _has_source, _api_error, | ||||
|     Address(AddrErr):  _has_source, _api_error, | ||||
|     Smtp(SmtpErr):     _has_source, _api_error, | ||||
|     OpenSSL(SSLErr):   _has_source, _api_error, | ||||
|  | ||||
|     DieselCon(DieselConErr): _has_source, _api_error, | ||||
|     DieselMig(DieselMigErr): _has_source, _api_error, | ||||
|   | ||||
							
								
								
									
										68
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -21,7 +21,7 @@ use std::{ | ||||
|     fs::create_dir_all, | ||||
|     panic, | ||||
|     path::Path, | ||||
|     process::{exit, Command}, | ||||
|     process::exit, | ||||
|     str::FromStr, | ||||
|     thread, | ||||
|     time::Duration, | ||||
| @@ -53,7 +53,10 @@ fn main() { | ||||
|     let extra_debug = matches!(level, LF::Trace | LF::Debug); | ||||
|  | ||||
|     check_data_folder(); | ||||
|     check_rsa_keys(); | ||||
|     check_rsa_keys().unwrap_or_else(|_| { | ||||
|         error!("Error creating keys, exiting..."); | ||||
|         exit(1); | ||||
|     }); | ||||
|     check_web_vault(); | ||||
|  | ||||
|     create_icon_cache_folder(); | ||||
| @@ -249,52 +252,29 @@ fn check_data_folder() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn check_rsa_keys() { | ||||
| fn check_rsa_keys()-> Result<(), crate::error::Error> { | ||||
|     // If the RSA keys don't exist, try to create them | ||||
|     if !util::file_exists(&CONFIG.private_rsa_key()) || !util::file_exists(&CONFIG.public_rsa_key()) { | ||||
|         info!("JWT keys don't exist, checking if OpenSSL is available..."); | ||||
|     let priv_path = CONFIG.private_rsa_key(); | ||||
|     let pub_path = CONFIG.public_rsa_key(); | ||||
|  | ||||
|         Command::new("openssl").arg("version").status().unwrap_or_else(|_| { | ||||
|             info!( | ||||
|                 "Can't create keys because OpenSSL is not available, make sure it's installed and available on the PATH" | ||||
|             ); | ||||
|             exit(1); | ||||
|         }); | ||||
|     if !util::file_exists(&priv_path) { | ||||
|         let rsa_key = openssl::rsa::Rsa::generate(2048)?; | ||||
|  | ||||
|         info!("OpenSSL detected, creating keys..."); | ||||
|  | ||||
|         let key = CONFIG.rsa_key_filename(); | ||||
|  | ||||
|         let pem = format!("{}.pem", key); | ||||
|         let priv_der = format!("{}.der", key); | ||||
|         let pub_der = format!("{}.pub.der", key); | ||||
|  | ||||
|         let mut success = Command::new("openssl") | ||||
|             .args(&["genrsa", "-out", &pem]) | ||||
|             .status() | ||||
|             .expect("Failed to create private pem file") | ||||
|             .success(); | ||||
|  | ||||
|         success &= Command::new("openssl") | ||||
|             .args(&["rsa", "-in", &pem, "-outform", "DER", "-out", &priv_der]) | ||||
|             .status() | ||||
|             .expect("Failed to create private der file") | ||||
|             .success(); | ||||
|  | ||||
|         success &= Command::new("openssl") | ||||
|             .args(&["rsa", "-in", &priv_der, "-inform", "DER"]) | ||||
|             .args(&["-RSAPublicKey_out", "-outform", "DER", "-out", &pub_der]) | ||||
|             .status() | ||||
|             .expect("Failed to create public der file") | ||||
|             .success(); | ||||
|  | ||||
|         if success { | ||||
|             info!("Keys created correctly."); | ||||
|         } else { | ||||
|             error!("Error creating keys, exiting..."); | ||||
|             exit(1); | ||||
|         } | ||||
|         let priv_key = rsa_key.private_key_to_pem()?; | ||||
|         crate::util::write_file(&priv_path, &priv_key)?; | ||||
|         info!("Private key created correctly."); | ||||
|     } | ||||
|  | ||||
|     if !util::file_exists(&pub_path) { | ||||
|         let rsa_key = openssl::rsa::Rsa::private_key_from_pem(&util::read_file(&priv_path)?)?; | ||||
|  | ||||
|         let pub_key = rsa_key.public_key_to_pem()?; | ||||
|         crate::util::write_file(&pub_path, &pub_key)?; | ||||
|         info!("Public key created correctly."); | ||||
|     } | ||||
|  | ||||
|     auth::load_keys(); | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn check_web_vault() { | ||||
|   | ||||
| @@ -219,6 +219,14 @@ pub fn read_file(path: &str) -> IOResult<Vec<u8>> { | ||||
|     Ok(contents) | ||||
| } | ||||
|  | ||||
| pub fn write_file(path: &str, content: &[u8]) -> Result<(), crate::error::Error> { | ||||
|     use std::io::Write; | ||||
|     let mut f = File::create(path)?; | ||||
|     f.write_all(content)?; | ||||
|     f.flush()?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub fn read_file_string(path: &str) -> IOResult<String> { | ||||
|     let mut contents = String::new(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user