Skip to content

Commit

Permalink
Load RSA keys as pem format directly, and using openssl crate, backpo…
Browse files Browse the repository at this point in the history
…rted from async branch
  • Loading branch information
dani-garcia committed Jun 25, 2021
1 parent 2cd17fe commit 46e0f3c
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 61 deletions.
5 changes: 2 additions & 3 deletions src/api/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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>")]
Expand Down
2 changes: 1 addition & 1 deletion src/api/core/two_factor/duo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
Expand Down
28 changes: 17 additions & 11 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand All @@ -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> {
Expand Down
5 changes: 1 addition & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down Expand Up @@ -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,
Expand Down
64 changes: 22 additions & 42 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::{
fs::create_dir_all,
panic,
path::Path,
process::{exit, Command},
process::exit,
str::FromStr,
thread,
time::Duration,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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...");

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);
});

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 priv_path = CONFIG.private_rsa_key();
let pub_path = CONFIG.public_rsa_key();

let mut success = Command::new("openssl")
.args(&["genrsa", "-out", &pem])
.status()
.expect("Failed to create private pem file")
.success();
if !util::file_exists(&priv_path) {
let rsa_key = openssl::rsa::Rsa::generate(2048)?;

success &= Command::new("openssl")
.args(&["rsa", "-in", &pem, "-outform", "DER", "-out", &priv_der])
.status()
.expect("Failed to create private der file")
.success();
let priv_key = rsa_key.private_key_to_pem()?;
crate::util::write_file(&priv_path, &priv_key)?;
info!("Private key created correctly.");
}

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 !util::file_exists(&pub_path) {
let rsa_key = openssl::rsa::Rsa::private_key_from_pem(&util::read_file(&priv_path)?)?;

if success {
info!("Keys created correctly.");
} else {
error!("Error creating keys, exiting...");
exit(1);
}
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() {
Expand Down
8 changes: 8 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down

0 comments on commit 46e0f3c

Please sign in to comment.