Skip to content

Commit

Permalink
add argon2 kdf fields
Browse files Browse the repository at this point in the history
  • Loading branch information
tessus committed Feb 7, 2023
1 parent 9366e31 commit d4b51ca
Show file tree
Hide file tree
Showing 16 changed files with 126 additions and 24 deletions.
Empty file.
7 changes: 7 additions & 0 deletions migrations/mysql/2023-01-31-222222_add_argon2/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE users
ADD COLUMN
client_kdf_memory INTEGER DEFAULT NULL;

ALTER TABLE users
ADD COLUMN
client_kdf_parallelism INTEGER DEFAULT NULL;
Empty file.
7 changes: 7 additions & 0 deletions migrations/postgresql/2023-01-31-222222_add_argon2/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE users
ADD COLUMN
client_kdf_memory INTEGER DEFAULT NULL;

ALTER TABLE users
ADD COLUMN
client_kdf_parallelism INTEGER DEFAULT NULL;
Empty file.
7 changes: 7 additions & 0 deletions migrations/sqlite/2023-01-31-222222_add_argon2/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE users
ADD COLUMN
client_kdf_memory INTEGER DEFAULT NULL;

ALTER TABLE users
ADD COLUMN
client_kdf_parallelism INTEGER DEFAULT NULL;
60 changes: 50 additions & 10 deletions src/api/core/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub struct RegisterData {
Email: String,
Kdf: Option<i32>,
KdfIterations: Option<i32>,
KdfMemory: Option<i32>,
KdfParallelism: Option<i32>,
Key: String,
Keys: Option<KeysData>,
MasterPasswordHash: String,
Expand Down Expand Up @@ -153,12 +155,20 @@ pub async fn _register(data: JsonUpcase<RegisterData>, mut conn: DbConn) -> Json
// Make sure we don't leave a lingering invitation.
Invitation::take(&email, &mut conn).await;

if let Some(client_kdf_type) = data.Kdf {
user.client_kdf_type = client_kdf_type;
}

if let Some(client_kdf_iter) = data.KdfIterations {
user.client_kdf_iter = client_kdf_iter;
}

if let Some(client_kdf_type) = data.Kdf {
user.client_kdf_type = client_kdf_type;
if let Some(client_kdf_memory) = data.KdfMemory {
user.client_kdf_memory = Some(client_kdf_memory);
}

if let Some(client_kdf_parallelism) = data.KdfParallelism {
user.client_kdf_parallelism = Some(client_kdf_parallelism);
}

user.set_password(&data.MasterPasswordHash, Some(data.Key), true, None);
Expand Down Expand Up @@ -337,6 +347,8 @@ async fn post_password(
struct ChangeKdfData {
Kdf: i32,
KdfIterations: i32,
KdfMemory: Option<i32>,
KdfParallelism: Option<i32>,

MasterPasswordHash: String,
NewMasterPasswordHash: String,
Expand All @@ -352,10 +364,31 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D
err!("Invalid password")
}

if data.KdfIterations < 100_000 {
err!("KDF iterations lower then 100000 are not allowed.")
if data.Kdf == UserKdfType::Pbkdf2 as i32 && data.KdfIterations < 100_000 {
err!("PBKDF2 KDF iterations must be at least 100000.")
}

if data.Kdf == UserKdfType::Argon2id as i32 {
if data.KdfIterations < 1 {
err!("Argon2 KDF iterations must be at least 1.")
}
if let Some(m) = data.KdfMemory {
if m < 15 || m > 1024 {
err!("Argon2 memory must be between 15 MB and 1024 MB.")
}
user.client_kdf_memory = data.KdfMemory;
} else {
err!("Argon2 memory parameter is required.")
}
if let Some(p) = data.KdfParallelism {
if p < 1 || p > 16 {
err!("Argon2 parallelism must be between 1 and 16.")
}
user.client_kdf_parallelism = data.KdfParallelism;
} else {
err!("Argon2 parallelism parameter is required.")
}
}
user.client_kdf_iter = data.KdfIterations;
user.client_kdf_type = data.Kdf;
user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None);
Expand Down Expand Up @@ -770,15 +803,22 @@ async fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> {
pub async fn _prelogin(data: JsonUpcase<PreloginData>, mut conn: DbConn) -> Json<Value> {
let data: PreloginData = data.into_inner().data;

let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &mut conn).await {
Some(user) => (user.client_kdf_type, user.client_kdf_iter),
None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT),
let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &mut conn).await {
Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism),
None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None),
};

Json(json!({
let mut result = json!({
"Kdf": kdf_type,
"KdfIterations": kdf_iter
}))
"KdfIterations": kdf_iter,
});

if kdf_type == UserKdfType::Argon2id as i32 {
result["KdfMemory"] = Value::Number(kdf_mem.unwrap().into());
result["KdfParallelism"] = Value::Number(kdf_para.unwrap().into());
}

Json(result)
}

// https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs
Expand Down
19 changes: 13 additions & 6 deletions src/api/core/emergency_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,12 +618,19 @@ async fn takeover_emergency_access(emer_id: String, headers: Headers, mut conn:
None => err!("Grantor user not found."),
};

Ok(Json(json!({
"Kdf": grantor_user.client_kdf_type,
"KdfIterations": grantor_user.client_kdf_iter,
"KeyEncrypted": &emergency_access.key_encrypted,
"Object": "emergencyAccessTakeover",
})))
let mut result = json!({
"Kdf": grantor_user.client_kdf_type,
"KdfIterations": grantor_user.client_kdf_iter,
"KeyEncrypted": &emergency_access.key_encrypted,
"Object": "emergencyAccessTakeover",
});

if grantor_user.client_kdf_type == UserKdfType::Argon2id as i32 {
result["KdfMemory"] = Value::Number(grantor_user.client_kdf_memory.unwrap().into());
result["KdfParallelism"] = Value::Number(grantor_user.client_kdf_parallelism.unwrap().into());
}

Ok(Json(result))
}

#[derive(Deserialize)]
Expand Down
27 changes: 23 additions & 4 deletions src/api/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult {
let (access_token, expires_in) = device.refresh_tokens(&user, orgs, scope_vec);
device.save(conn).await?;

Ok(Json(json!({
let mut result = json!({
"access_token": access_token,
"expires_in": expires_in,
"token_type": "Bearer",
Expand All @@ -109,7 +109,14 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult {
"ResetMasterPassword": false, // TODO: according to official server seems something like: user.password_hash.is_empty(), but would need testing
"scope": scope,
"unofficialServer": true,
})))
});

if user.client_kdf_type == UserKdfType::Argon2id as i32 {
result["KdfMemory"] = Value::Number(user.client_kdf_memory.unwrap().into());
result["KdfParallelism"] = Value::Number(user.client_kdf_parallelism.unwrap().into());
}

Ok(Json(result))
}

async fn _password_login(
Expand Down Expand Up @@ -249,6 +256,11 @@ async fn _password_login(
result["TwoFactorToken"] = Value::String(token);
}

if user.client_kdf_type == UserKdfType::Argon2id as i32 {
result["KdfMemory"] = Value::Number(user.client_kdf_memory.unwrap().into());
result["KdfParallelism"] = Value::Number(user.client_kdf_parallelism.unwrap().into());
}

info!("User {} logged in successfully. IP: {}", username, ip.ip);
Ok(Json(result))
}
Expand Down Expand Up @@ -333,7 +345,7 @@ async fn _api_key_login(

// Note: No refresh_token is returned. The CLI just repeats the
// client_credentials login flow when the existing token expires.
Ok(Json(json!({
let mut result = json!({
"access_token": access_token,
"expires_in": expires_in,
"token_type": "Bearer",
Expand All @@ -345,7 +357,14 @@ async fn _api_key_login(
"ResetMasterPassword": false, // TODO: Same as above
"scope": scope,
"unofficialServer": true,
})))
});

if user.client_kdf_type == UserKdfType::Argon2id as i32 {
result["KdfMemory"] = Value::Number(user.client_kdf_memory.unwrap().into());
result["KdfParallelism"] = Value::Number(user.client_kdf_parallelism.unwrap().into());
}

Ok(Json(result))
}

/// Retrieves an existing device or creates a new device from ConnectData and the User
Expand Down
2 changes: 1 addition & 1 deletion src/db/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ pub use self::organization::{Organization, UserOrgStatus, UserOrgType, UserOrgan
pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_incomplete::TwoFactorIncomplete;
pub use self::user::{Invitation, User, UserStampException};
pub use self::user::{Invitation, User, UserKdfType, UserStampException};
11 changes: 10 additions & 1 deletion src/db/models/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ db_object! {

pub client_kdf_type: i32,
pub client_kdf_iter: i32,
pub client_kdf_memory: Option<i32>,
pub client_kdf_parallelism: Option<i32>,

pub api_key: Option<String>,

Expand All @@ -58,6 +60,11 @@ db_object! {
}
}

pub enum UserKdfType {
Pbkdf2 = 0,
Argon2id = 1,
}

enum UserStatus {
Enabled = 0,
Invited = 1,
Expand All @@ -73,7 +80,7 @@ pub struct UserStampException {

/// Local methods
impl User {
pub const CLIENT_KDF_TYPE_DEFAULT: i32 = 0; // PBKDF2: 0
pub const CLIENT_KDF_TYPE_DEFAULT: i32 = UserKdfType::Pbkdf2 as i32;
pub const CLIENT_KDF_ITER_DEFAULT: i32 = 600_000;

pub fn new(email: String) -> Self {
Expand Down Expand Up @@ -113,6 +120,8 @@ impl User {

client_kdf_type: Self::CLIENT_KDF_TYPE_DEFAULT,
client_kdf_iter: Self::CLIENT_KDF_ITER_DEFAULT,
client_kdf_memory: None,
client_kdf_parallelism: None,

api_key: None,

Expand Down
2 changes: 2 additions & 0 deletions src/db/schemas/mysql/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ table! {
excluded_globals -> Text,
client_kdf_type -> Integer,
client_kdf_iter -> Integer,
client_kdf_memory -> Nullable<Integer>,
client_kdf_parallelism -> Nullable<Integer>,
api_key -> Nullable<Text>,
avatar_color -> Nullable<Text>,
}
Expand Down
2 changes: 2 additions & 0 deletions src/db/schemas/postgresql/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ table! {
excluded_globals -> Text,
client_kdf_type -> Integer,
client_kdf_iter -> Integer,
client_kdf_memory -> Nullable<Integer>,
client_kdf_parallelism -> Nullable<Integer>,
api_key -> Nullable<Text>,
avatar_color -> Nullable<Text>,
}
Expand Down
2 changes: 2 additions & 0 deletions src/db/schemas/sqlite/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ table! {
excluded_globals -> Text,
client_kdf_type -> Integer,
client_kdf_iter -> Integer,
client_kdf_memory -> Nullable<Integer>,
client_kdf_parallelism -> Nullable<Integer>,
api_key -> Nullable<Text>,
avatar_color -> Nullable<Text>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
// The more key/value pairs there are the more recursion occurs.
// We want to keep this as low as possible, but not higher then 128.
// If you go above 128 it will cause rust-analyzer to fail,
#![recursion_limit = "97"]
#![recursion_limit = "103"]

// When enabled use MiMalloc as malloc instead of the default malloc
#[cfg(feature = "enable_mimalloc")]
Expand Down
2 changes: 1 addition & 1 deletion src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Fairing for AppHeaders {
base-uri 'self'; \
form-action 'self'; \
object-src 'self' blob:; \
script-src 'self'; \
script-src 'self' 'wasm-unsafe-eval'; \
style-src 'self' 'unsafe-inline'; \
child-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; \
Expand Down

0 comments on commit d4b51ca

Please sign in to comment.