commit 3dae594e30d1de8fa270f4d58142d6c05d88e455
parent 1036fcb53c4877f6bd1c4fc608064dc66f87a447
Author: Zack Newman <zack@philomathiclife.com>
Date: Mon, 11 Dec 2023 12:22:13 -0700
use ed25519 for jwt. fix openbsd compile error
Diffstat:
4 files changed, 41 insertions(+), 35 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -38,7 +38,7 @@ rocket_ws = { version = "0.1.0", default-features = false, features = ["tokio-tu
semver = { version = "1.0.20", default-features = false }
serde = { version = "1.0.193", default-features = false }
serde_json = { version = "1.0.108", default-features = false }
-tokio = { version = "1.34.0", default-features = false }
+tokio = { version = "1.35.0", default-features = false }
tokio-tungstenite = { version = "0.20.1", default-features = false }
toml = { version = "0.8.8", default-features = false, features = ["parse"] }
totp-lite = { version = "2.0.1", default-features = false }
diff --git a/src/auth.rs b/src/auth.rs
@@ -5,14 +5,13 @@ use crate::{
use chrono::{Duration, Utc};
use jsonwebtoken::{self, errors::ErrorKind, Algorithm, DecodingKey, EncodingKey, Header};
use num_traits::FromPrimitive;
-use openssl::rsa::Rsa;
+use openssl::pkey::{Id, PKey};
use serde::de::DeserializeOwned;
use serde::ser::Serialize;
use std::fs::File;
use std::io::{Read, Write};
use std::sync::OnceLock;
-const JWT_ALGORITHM: Algorithm = Algorithm::RS256;
static DEFAULT_VALIDITY: OnceLock<Duration> = OnceLock::new();
#[inline]
fn init_default_validity() {
@@ -132,47 +131,52 @@ fn get_jwt_file_download_issuer() -> &'static str {
.expect("JWT_FILE_DOWNLOAD_ISSUER must be initialized in main")
.as_str()
}
-static RSA_KEYS: OnceLock<(EncodingKey, DecodingKey)> = OnceLock::new();
-#[allow(clippy::verbose_file_reads)]
+const JWT_ALGORITHM: Algorithm = Algorithm::EdDSA;
+static ED_KEYS: OnceLock<(EncodingKey, DecodingKey)> = OnceLock::new();
+#[allow(clippy::map_err_ignore, clippy::verbose_file_reads)]
#[inline]
-fn init_rsa_keys() -> Result<(), Error> {
- let mut priv_file = File::options()
+fn init_ed_keys() -> Result<(), Error> {
+ let mut file = File::options()
.create(true)
.read(true)
.write(true)
- .open(Config::PRIVATE_RSA_KEY)?;
- let mut priv_pem = Vec::with_capacity(2048);
- let priv_key = if priv_file.read_to_end(&mut priv_pem)? == 0 {
- let rsa_key = openssl::rsa::Rsa::generate(2048)?;
- priv_pem = rsa_key.private_key_to_pem()?;
- priv_file.write_all(priv_pem.as_slice())?;
- rsa_key
+ .open(Config::PRIVATE_ED25519_KEY)?;
+ let mut priv_pem = Vec::with_capacity(192);
+ let ed_key = if file.read_to_end(&mut priv_pem)? == 0 {
+ let ed_key = PKey::generate_ed25519()?;
+ priv_pem = ed_key.private_key_to_pem_pkcs8()?;
+ file.write_all(priv_pem.as_slice())?;
+ ed_key
} else {
- Rsa::private_key_from_pem(priv_pem.as_slice())?
+ let ed_key = PKey::private_key_from_pem(priv_pem.as_slice())?;
+ if ed_key.id() == Id::ED25519 {
+ ed_key
+ } else {
+ return Err(Error::from(format!(
+ "{} is not a private Ed25519 key",
+ Config::PRIVATE_ED25519_KEY
+ )));
+ }
};
- assert!(
- RSA_KEYS
- .set((
- EncodingKey::from_rsa_pem(priv_pem.as_slice())?,
- DecodingKey::from_rsa_pem(priv_key.public_key_to_pem()?.as_slice())?
- ))
- .is_ok(),
- "RSA_KEYS must only be initialized once"
- );
- Ok(())
+ ED_KEYS
+ .set((
+ EncodingKey::from_ed_pem(priv_pem.as_slice())?,
+ DecodingKey::from_ed_pem(ed_key.public_key_to_pem()?.as_slice())?,
+ ))
+ .map_err(|_| Error::from(String::from("ED_KEYS must only be initialized once")))
}
#[inline]
-fn get_private_rsa_key() -> &'static EncodingKey {
- &RSA_KEYS
+fn get_private_ed_key() -> &'static EncodingKey {
+ &ED_KEYS
.get()
- .expect("RSA_KEYS must be initialized in main")
+ .expect("ED_KEYS must be initialized in main")
.0
}
#[inline]
-fn get_public_rsa_key() -> &'static DecodingKey {
- &RSA_KEYS
+fn get_public_ed_key() -> &'static DecodingKey {
+ &ED_KEYS
.get()
- .expect("RSA_KEYS must be initialized in main")
+ .expect("ED_KEYS must be initialized in main")
.1
}
#[inline]
@@ -185,10 +189,10 @@ pub fn init_values() {
init_jwt_verifyemail_issuer();
init_jwt_org_api_key_issuer();
init_jwt_file_download_issuer();
- init_rsa_keys().expect("error creating or reading RSA keys");
+ init_ed_keys().expect("error creating Ed25519 keys");
}
pub fn encode_jwt<T: Serialize>(claims: &T) -> String {
- match jsonwebtoken::encode(get_jwt_header(), claims, get_private_rsa_key()) {
+ match jsonwebtoken::encode(get_jwt_header(), claims, get_private_ed_key()) {
Ok(token) => token,
Err(e) => panic!("Error encoding jwt {e}"),
}
@@ -202,7 +206,7 @@ fn decode_jwt<T: DeserializeOwned>(token: &str, issuer: String) -> Result<T, Err
validation.validate_nbf = true;
validation.set_issuer(&[issuer]);
let token = token.replace(char::is_whitespace, "");
- match jsonwebtoken::decode(&token, get_public_rsa_key(), &validation) {
+ match jsonwebtoken::decode(&token, get_public_ed_key(), &validation) {
Ok(d) => Ok(d.claims),
Err(err) => match *err.kind() {
ErrorKind::InvalidToken => err!("Token is invalid"),
diff --git a/src/config.rs b/src/config.rs
@@ -173,7 +173,7 @@ impl Config {
pub const DATA_FOLDER: &'static str = "data";
pub const DATABASE_URL: &'static str = "data/db.sqlite3";
pub const ICON_CACHE_FOLDER: &'static str = "data/icon_cache";
- pub const PRIVATE_RSA_KEY: &'static str = "data/rsa_key.pem";
+ pub const PRIVATE_ED25519_KEY: &'static str = "data/ed25519_key.pem";
pub const SENDS_FOLDER: &'static str = "data/sends";
pub const TMP_FOLDER: &'static str = "data/tmp";
pub const WEB_VAULT_FOLDER: &'static str = "web-vault/";
diff --git a/src/error.rs b/src/error.rs
@@ -130,6 +130,8 @@ impl std::fmt::Debug for Error {
write!(f, "{}. {}", self.message, s)
}
}
+ #[cfg(all(feature = "priv_sep", target_os = "openbsd"))]
+ ErrorKind::Unveil(ref err) => err.fmt(f),
ErrorKind::Json(_) => write!(f, "{}", self.message),
ErrorKind::Db(_)
| ErrorKind::R2d2(_)