diff --git a/Cargo.lock b/Cargo.lock index fa7f4c99a..927a3296c 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/Cargo.toml b/Cargo.toml index c8ee2782c..4761128e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ # "crates/p2p/tunnel", # "crates/p2p/tunnel/utils", "crates/sync/example/api", + "apps/cli", "apps/desktop/src-tauri", "apps/mobile/rust", "apps/server", diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml new file mode 100644 index 000000000..5707542af --- /dev/null +++ b/apps/cli/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cli" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +indoc = "1.0.8" +clap = { version = "4.0.32", features = ["derive"] } +anyhow = "1.0.68" +hex = "0.4.3" +sd-crypto = { path = "../../crates/crypto" } diff --git a/apps/cli/README.md b/apps/cli/README.md new file mode 100644 index 000000000..cb5156840 --- /dev/null +++ b/apps/cli/README.md @@ -0,0 +1,4 @@ +# CLI + +Basic CLI for interacting with encrypted files. +Will be expanded to a general Spacedrive CLI in the future. diff --git a/apps/cli/src/main.rs b/apps/cli/src/main.rs new file mode 100644 index 000000000..d35d67a6b --- /dev/null +++ b/apps/cli/src/main.rs @@ -0,0 +1,83 @@ +use anyhow::{Context, Result}; +use clap::Parser; +use indoc::printdoc; +use sd_crypto::header::file::FileHeader; +use std::{fs::File, path::PathBuf}; + +#[derive(Parser)] +struct Args { + #[arg(help = "the file path to get details for")] + path: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let mut reader = File::open(args.path).context("unable to open file")?; + let (header, aad) = FileHeader::from_reader(&mut reader)?; + print_details(&header, &aad); + + Ok(()) +} + +fn print_details(header: &FileHeader, aad: &[u8]) { + printdoc! {" + Header version: {version} + Encryption algorithm: {algorithm} + AAD (hex): {hex} + ", + version = header.version, + algorithm = header.algorithm, + hex = hex::encode(aad) + }; + + header.keyslots.iter().enumerate().for_each(|(i, k)| { + printdoc! {" + Keyslot {index}: + Version: {version} + Algorithm: {algorithm} + Hashing algorithm: {hashing_algorithm} + Salt (hex): {salt} + Master Key (hex, encrypted): {master} + Master key nonce (hex): {nonce} + ", + index = i + i, + version = k.version, + algorithm = k.algorithm, + hashing_algorithm = k.hashing_algorithm, + salt = hex::encode(k.salt), + master = hex::encode(k.master_key), + nonce = hex::encode(k.nonce.clone()) + }; + }); + + header.metadata.iter().for_each(|m| { + printdoc! {" + Metadata: + Version: {version} + Algorithm: {algorithm} + Encrypted size: {size} + Nonce (hex): {nonce} + ", + version = m.version, + algorithm = m.algorithm, + size = m.metadata.len(), + nonce = hex::encode(m.metadata_nonce.clone()) + } + }); + + header.preview_media.iter().for_each(|p| { + printdoc! {" + Preview Media: + Version: {version} + Algorithm: {algorithm} + Encrypted size: {size} + Nonce (hex): {nonce} + ", + version = p.version, + algorithm = p.algorithm, + size = p.media.len(), + nonce = hex::encode(p.media_nonce.clone()) + }; + }); +} diff --git a/core/src/library/library_manager.rs b/core/src/library/library_manager.rs index b929ddea4..333976d3b 100644 --- a/core/src/library/library_manager.rs +++ b/core/src/library/library_manager.rs @@ -76,6 +76,7 @@ pub async fn create_keymanager( client: &PrismaClient, ) -> Result, LibraryManagerError> { let key_manager = KeyManager::new(vec![])?; + let mut default: Option = None; // collect and serialize the stored keys @@ -97,13 +98,13 @@ pub async fn create_keymanager( let stored_key = StoredKey { uuid, - algorithm: Algorithm::deserialize(to_array(key.algorithm)?)?, + algorithm: Algorithm::from_bytes(to_array(key.algorithm)?)?, content_salt: to_array(key.content_salt)?, master_key: to_array(key.master_key)?, master_key_nonce: key.master_key_nonce, key_nonce: key.key_nonce, key: key.key, - hashing_algorithm: HashingAlgorithm::deserialize(to_array(key.hashing_algorithm)?)?, + hashing_algorithm: HashingAlgorithm::from_bytes(to_array(key.hashing_algorithm)?)?, salt: to_array(key.salt)?, memory_only: false, automount: key.automount, diff --git a/core/src/object/fs/decrypt.rs b/core/src/object/fs/decrypt.rs index 7b46d9027..c951acfea 100644 --- a/core/src/object/fs/decrypt.rs +++ b/core/src/object/fs/decrypt.rs @@ -118,7 +118,7 @@ impl StatefulJob for FileDecryptorJob { let mut reader = std::fs::File::open(step.obj_path.clone())?; let mut writer = std::fs::File::create(output_path)?; - let (header, aad) = FileHeader::deserialize(&mut reader)?; + let (header, aad) = FileHeader::from_reader(&mut reader)?; let master_key = if let Some(password) = state.init.password.clone() { if let Some(save_to_library) = state.init.save_to_library { diff --git a/core/src/object/fs/encrypt.rs b/core/src/object/fs/encrypt.rs index e3b40de90..b247146d6 100644 --- a/core/src/object/fs/encrypt.rs +++ b/core/src/object/fs/encrypt.rs @@ -171,7 +171,7 @@ impl StatefulJob for FileEncryptorJob { user_key_details.hashing_algorithm, user_key_details.content_salt, user_key, - &master_key, + master_key.clone(), )?]; let mut header = @@ -203,7 +203,7 @@ impl StatefulJob for FileEncryptorJob { header.add_metadata( LATEST_METADATA, state.init.algorithm, - &master_key, + master_key.clone(), &metadata, )?; } diff --git a/core/src/util/db.rs b/core/src/util/db.rs index 57227a5e8..2cf0a54cb 100644 --- a/core/src/util/db.rs +++ b/core/src/util/db.rs @@ -50,8 +50,8 @@ pub async fn write_storedkey_to_db(db: &PrismaClient, key: &StoredKey) -> Result db.key() .create( key.uuid.to_string(), - key.algorithm.serialize().to_vec(), - key.hashing_algorithm.serialize().to_vec(), + key.algorithm.to_bytes().to_vec(), + key.hashing_algorithm.to_bytes().to_vec(), key.content_salt.to_vec(), key.master_key.to_vec(), key.master_key_nonce.to_vec(), diff --git a/crates/crypto-cli/Cargo.toml b/crates/crypto-cli/Cargo.toml deleted file mode 100644 index dc2dcc147..000000000 --- a/crates/crypto-cli/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "crypto-cli" -version = "0.0.0" -edition = "2021" -authors = ["Jake Robinson "] -description = "A CLI tool to view encrypted file details (for files encrypted with Spacedrive)" -rust-version = "1.64.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -clap = { version = "4.0.32", features = ["derive"] } -sd-crypto = { path = "../crypto", features = ["serde"] } -anyhow = "1.0.68" -hex = "0.4.3" diff --git a/crates/crypto-cli/src/main.rs b/crates/crypto-cli/src/main.rs deleted file mode 100644 index f6c1ff40f..000000000 --- a/crates/crypto-cli/src/main.rs +++ /dev/null @@ -1,53 +0,0 @@ -use anyhow::{Context, Result}; -use clap::Parser; -use sd_crypto::header::file::FileHeader; -use std::{fs::File, path::PathBuf}; - -#[derive(Parser)] -struct Args { - #[arg(help = "the file path to get details for")] - path: PathBuf, -} - -fn main() -> Result<()> { - let args = Args::parse(); - - let mut reader = File::open(args.path).context("unable to open file")?; - let (header, aad) = FileHeader::deserialize(&mut reader)?; - print_details(&header, &aad)?; - - Ok(()) -} - -fn print_details(header: &FileHeader, aad: &[u8]) -> Result<()> { - println!("Header version: {}", header.version); - println!("Encryption algorithm: {}", header.algorithm); - println!("AAD (hex): {}", hex::encode(aad)); - header.keyslots.iter().enumerate().for_each(|(i, k)| { - println!("Keyslot {}:", i + 1); - println!(" Version: {}", k.version); - println!(" Algorithm: {}", k.algorithm); - println!(" Hashing algorithm: {}", k.hashing_algorithm); - println!(" Salt (hex): {}", hex::encode(k.salt)); - println!(" Master Key (hex, encrypted): {}", hex::encode(k.master_key)); - println!(" Master key nonce (hex): {}", hex::encode(k.nonce.clone())); - }); - - header.metadata.clone().iter().for_each(|m| { - println!("Metadata:"); - println!(" Version: {}", m.version); - println!(" Algorithm: {}", m.algorithm); - println!(" Encrypted size: {}", m.metadata.len()); - println!(" Nonce (hex): {}", hex::encode(m.metadata_nonce.clone())); - }); - - header.preview_media.clone().iter().for_each(|p| { - println!("Preview Media:"); - println!(" Version: {}", p.version); - println!(" Algorithm: {}", p.algorithm); - println!(" Encrypted size: {}", p.media.len()); - println!(" Nonce (hex): {}", hex::encode(p.media_nonce.clone())) - }); - - Ok(()) -} diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index a5eb7a94b..22e9cd982 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -14,7 +14,8 @@ rand_chacha = "0.3.1" # hashing argon2 = "0.4.1" -blake3 = "1.3.3" +balloon-hash = "0.3.0" +blake3 = { version = "1.3.3", features = ["traits-preview"] } # aeads aes-gcm = "0.10.1" diff --git a/crates/crypto/benches/argon2id.rs b/crates/crypto/benches/argon2id.rs index a6fd7ba99..65d7408db 100644 --- a/crates/crypto/benches/argon2id.rs +++ b/crates/crypto/benches/argon2id.rs @@ -15,16 +15,13 @@ fn bench(c: &mut Criterion) { let salt = generate_salt(); let hashing_algorithm = HashingAlgorithm::Argon2id(param); - group.bench_function( - BenchmarkId::new("hash", param.get_argon2_params().m_cost()), - |b| { - b.iter_batched( - || (key.clone(), salt.clone()), - |(key, salt)| hashing_algorithm.hash(key, salt), - BatchSize::SmallInput, - ) - }, - ); + group.bench_function(BenchmarkId::new("hash", param.argon2id().m_cost()), |b| { + b.iter_batched( + || (key.clone(), salt.clone()), + |(key, salt)| hashing_algorithm.hash(key, salt), + BatchSize::SmallInput, + ) + }); } group.finish(); diff --git a/crates/crypto/examples/single_file.rs b/crates/crypto/examples/single_file.rs index a4f663cf3..8b3c17c51 100644 --- a/crates/crypto/examples/single_file.rs +++ b/crates/crypto/examples/single_file.rs @@ -32,7 +32,7 @@ pub fn encrypt() { HASHING_ALGORITHM, content_salt, hashed_password, - &master_key, + master_key.clone(), ) .unwrap()]; @@ -60,7 +60,7 @@ pub fn decrypt() { let mut writer = File::create("test.original").unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, aad) = FileHeader::deserialize(&mut reader).unwrap(); + let (header, aad) = FileHeader::from_reader(&mut reader).unwrap(); // Decrypt the master key with the user's password let master_key = header.decrypt_master_key(password).unwrap(); diff --git a/crates/crypto/examples/single_file_with_metadata.rs b/crates/crypto/examples/single_file_with_metadata.rs index 6f15308e3..d726f32bd 100644 --- a/crates/crypto/examples/single_file_with_metadata.rs +++ b/crates/crypto/examples/single_file_with_metadata.rs @@ -41,7 +41,7 @@ fn encrypt() { HASHING_ALGORITHM, content_salt, hashed_password, - &master_key, + master_key.clone(), ) .unwrap()]; @@ -52,7 +52,7 @@ fn encrypt() { .add_metadata( MetadataVersion::V1, ALGORITHM, - &master_key, + master_key.clone(), &embedded_metadata, ) .unwrap(); @@ -77,7 +77,7 @@ pub fn decrypt_metadata() { let mut reader = File::open("test.encrypted").unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, _) = FileHeader::deserialize(&mut reader).unwrap(); + let (header, _) = FileHeader::from_reader(&mut reader).unwrap(); // Decrypt the metadata let file_info: FileInformation = header.decrypt_metadata(password).unwrap(); diff --git a/crates/crypto/examples/single_file_with_preview_media.rs b/crates/crypto/examples/single_file_with_preview_media.rs index f55375f5c..4eb94fda7 100644 --- a/crates/crypto/examples/single_file_with_preview_media.rs +++ b/crates/crypto/examples/single_file_with_preview_media.rs @@ -32,7 +32,7 @@ fn encrypt() { HASHING_ALGORITHM, content_salt, hashed_password, - &master_key, + master_key.clone(), ) .unwrap()]; @@ -42,7 +42,12 @@ fn encrypt() { let mut header = FileHeader::new(LATEST_FILE_HEADER, ALGORITHM, keyslots); header - .add_preview_media(PreviewMediaVersion::V1, ALGORITHM, &master_key, &pvm_media) + .add_preview_media( + PreviewMediaVersion::V1, + ALGORITHM, + master_key.clone(), + &pvm_media, + ) .unwrap(); // Write the header to the file @@ -65,7 +70,7 @@ pub fn decrypt_preview_media() { let mut reader = File::open("test.encrypted").unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, _) = FileHeader::deserialize(&mut reader).unwrap(); + let (header, _) = FileHeader::from_reader(&mut reader).unwrap(); // Decrypt the preview media let media = header.decrypt_preview_media(password).unwrap(); diff --git a/crates/crypto/src/error.rs b/crates/crypto/src/error.rs index 722a77bca..520347606 100644 --- a/crates/crypto/src/error.rs +++ b/crates/crypto/src/error.rs @@ -18,7 +18,7 @@ pub type Result = std::result::Result; pub enum Error { #[error("not enough bytes were written to the output file")] WriteMismatch, - #[error("there was an error hashing the password")] + #[error("there was an error while password hashing")] PasswordHash, #[error("I/O error: {0}")] Io(#[from] std::io::Error), @@ -42,8 +42,8 @@ pub enum Error { MediaLengthParse, #[error("no preview media found")] NoPreviewMedia, - #[error("error while serializing/deserializing the metadata")] - MetadataDeSerialization, + #[error("error while serializing/deserializing an item")] + Serialization, #[error("no metadata found")] NoMetadata, #[error("tried adding too many keyslots to a header")] diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index dbe9e406d..b73f497b0 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -29,7 +29,7 @@ //! // Write the header to the file //! header.write(&mut writer).unwrap(); //! ``` -use std::io::{Cursor, Read, Seek, SeekFrom, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use crate::{ crypto::stream::Algorithm, @@ -110,22 +110,15 @@ impl FileHeader { &self, password: Protected>, ) -> Result> { - let mut master_key: Option> = None; - if self.keyslots.is_empty() { return Err(Error::NoKeyslots); } - for keyslot in &self.keyslots { - if let Ok(decrypted_master_key) = keyslot.decrypt_master_key(&password) { - master_key = Some(Protected::new(to_array( - decrypted_master_key.expose().clone(), - )?)); - break; - } - } - - master_key.ok_or(Error::IncorrectPassword) + self.keyslots + .iter() + .find_map(|v| v.decrypt_master_key(password.clone()).ok()) + .map(|v| Protected::new(to_array::(v.expose().clone()).unwrap())) + .ok_or(Error::IncorrectPassword) } /// This is a helper function to find which keyslot a key belongs to. @@ -137,21 +130,19 @@ impl FileHeader { return Err(Error::NoKeyslots); } - for (i, keyslot) in self.keyslots.clone().iter().enumerate() { - if keyslot.decrypt_master_key(&password).is_ok() { - return Ok(i); - } - } - - Err(Error::IncorrectPassword) + self.keyslots + .iter() + .enumerate() + .find_map(|(i, v)| v.decrypt_master_key(password.clone()).ok().map(|_| i)) + .ok_or(Error::IncorrectPassword) } /// This is a helper function to serialize and write a header to a file. pub fn write(&self, writer: &mut W) -> Result<()> where - W: Write + Seek, + W: Write, { - writer.write_all(&self.serialize()?)?; + writer.write_all(&self.to_bytes()?)?; Ok(()) } @@ -165,26 +156,20 @@ impl FileHeader { &self, hashed_keys: Vec>, ) -> Result> { - let mut master_key: Option> = None; - if self.keyslots.is_empty() { return Err(Error::NoKeyslots); } - 'full: for key in hashed_keys { - for keyslot in &self.keyslots { - if let Ok(decrypted_master_key) = - keyslot.decrypt_master_key_from_prehashed(key.clone()) - { - master_key = Some(Protected::new(to_array( - decrypted_master_key.expose().clone(), - )?)); - break 'full; - } - } - } - - master_key.ok_or(Error::IncorrectPassword) + hashed_keys + .iter() + .find_map(|v| { + self.keyslots.iter().find_map(|z| { + z.decrypt_master_key_from_prehashed(v.clone()) + .ok() + .map(|x| Protected::new(to_array::(x.expose().clone()).unwrap())) + }) + }) + .ok_or(Error::IncorrectPassword) } /// This function should be used for generating AAD before encryption @@ -193,15 +178,17 @@ impl FileHeader { #[must_use] pub fn generate_aad(&self) -> Vec { match self.version { - FileHeaderVersion::V1 => { - let mut aad = Vec::new(); - aad.extend_from_slice(&MAGIC_BYTES); // 7 - aad.extend_from_slice(&self.version.serialize()); // 9 - aad.extend_from_slice(&self.algorithm.serialize()); // 11 - aad.extend_from_slice(&self.nonce); // 19 OR 31 - aad.extend_from_slice(&vec![0u8; 25 - self.nonce.len()]); // padded until 36 bytes - aad - } + FileHeaderVersion::V1 => vec![ + MAGIC_BYTES.as_ref(), + self.version.to_bytes().as_ref(), + self.algorithm.to_bytes().as_ref(), + self.nonce.as_ref(), + &vec![0u8; 25 - self.nonce.len()], + ] + .iter() + .flat_map(|&v| v) + .copied() + .collect(), } } @@ -210,7 +197,7 @@ impl FileHeader { /// This will include keyslots, metadata and preview media (if provided) /// /// An error will be returned if there are no keyslots/more than two keyslots attached. - pub fn serialize(&self) -> Result> { + pub fn to_bytes(&self) -> Result> { match self.version { FileHeaderVersion::V1 => { if self.keyslots.len() > 2 { @@ -219,28 +206,35 @@ impl FileHeader { return Err(Error::NoKeyslots); } - let mut header = Vec::new(); - header.extend_from_slice(&MAGIC_BYTES); // 7 - header.extend_from_slice(&self.version.serialize()); // 9 - header.extend_from_slice(&self.algorithm.serialize()); // 11 - header.extend_from_slice(&self.nonce); // 19 OR 31 - header.extend_from_slice(&vec![0u8; 25 - self.nonce.len()]); // padded until 36 bytes + let mut keyslots: Vec> = + self.keyslots.iter().map(Keyslot::to_bytes).collect(); - for keyslot in &self.keyslots { - header.extend_from_slice(&keyslot.serialize()); + if keyslots.len() == 1 { + keyslots.push(vec![0u8; KEYSLOT_SIZE]); } - for _ in 0..(2 - self.keyslots.len()) { - header.extend_from_slice(&[0u8; KEYSLOT_SIZE]); - } + let metadata = self.metadata.clone().map_or(Vec::new(), |v| v.to_bytes()); - if let Some(metadata) = self.metadata.clone() { - header.extend_from_slice(&metadata.serialize()); - } + let preview_media = self + .preview_media + .clone() + .map_or(Vec::new(), |v| v.to_bytes()); - if let Some(preview_media) = self.preview_media.clone() { - header.extend_from_slice(&preview_media.serialize()); - } + let header = vec![ + MAGIC_BYTES.as_ref(), + &self.version.to_bytes(), + &self.algorithm.to_bytes(), + &self.nonce, + &vec![0u8; 25 - self.nonce.len()], + &keyslots[0], + &keyslots[1], + &metadata, + &preview_media, + ] + .iter() + .flat_map(|&v| v) + .copied() + .collect(); Ok(header) } @@ -252,7 +246,7 @@ impl FileHeader { /// On error, the cursor will not be rewound. /// /// It returns both the header, and the AAD that should be used for decryption. - pub fn deserialize(reader: &mut R) -> Result<(Self, Vec)> + pub fn from_reader(reader: &mut R) -> Result<(Self, Vec)> where R: Read + Seek, { @@ -266,7 +260,7 @@ impl FileHeader { let mut version = [0u8; 2]; reader.read_exact(&mut version)?; - let version = FileHeaderVersion::deserialize(version)?; + let version = FileHeaderVersion::from_bytes(version)?; // Rewind so we can get the AAD reader.rewind()?; @@ -283,7 +277,7 @@ impl FileHeader { FileHeaderVersion::V1 => { let mut algorithm = [0u8; 2]; reader.read_exact(&mut algorithm)?; - let algorithm = Algorithm::deserialize(algorithm)?; + let algorithm = Algorithm::from_bytes(algorithm)?; let mut nonce = vec![0u8; algorithm.nonce_len()]; reader.read_exact(&mut nonce)?; @@ -295,15 +289,14 @@ impl FileHeader { let mut keyslots: Vec = Vec::new(); reader.read_exact(&mut keyslot_bytes)?; - let mut keyslot_reader = Cursor::new(keyslot_bytes); for _ in 0..2 { - if let Ok(keyslot) = Keyslot::deserialize(&mut keyslot_reader) { + if let Ok(keyslot) = Keyslot::from_reader(&mut keyslot_bytes.as_ref()) { keyslots.push(keyslot); } } - let metadata = if let Ok(metadata) = Metadata::deserialize(reader) { + let metadata = if let Ok(metadata) = Metadata::from_reader(reader) { Some(metadata) } else { // header/aad area, keyslot area @@ -313,7 +306,7 @@ impl FileHeader { None }; - let preview_media = if let Ok(preview_media) = PreviewMedia::deserialize(reader) { + let preview_media = if let Ok(preview_media) = PreviewMedia::from_reader(reader) { Some(preview_media) } else if let Some(metadata) = metadata.clone() { reader.seek(SeekFrom::Start( diff --git a/crates/crypto/src/header/keyslot.rs b/crates/crypto/src/header/keyslot.rs index 4b8493981..1096cb009 100644 --- a/crates/crypto/src/header/keyslot.rs +++ b/crates/crypto/src/header/keyslot.rs @@ -21,7 +21,7 @@ //! //! let keyslot = Keyslot::new(KeyslotVersion::V1, Algorithm::XChaCha20Poly1305, HashingAlgorithm::Argon2id(Params::Standard), user_password, &master_key).unwrap(); //! ``` -use std::io::{Read, Seek}; +use std::io::Read; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, @@ -63,13 +63,14 @@ impl Keyslot { /// This handles generating the nonce and encrypting the master key. /// /// You will need to provide the password, and a generated master key (this can't generate it, otherwise it can't be used elsewhere) + #[allow(clippy::needless_pass_by_value)] pub fn new( version: KeyslotVersion, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, content_salt: [u8; SALT_LEN], hashed_key: Protected<[u8; KEY_LEN]>, - master_key: &Protected<[u8; KEY_LEN]>, + master_key: Protected<[u8; KEY_LEN]>, ) -> Result { let nonce = generate_nonce(algorithm); @@ -100,10 +101,11 @@ impl Keyslot { /// This attempts to decrypt the master key for a single keyslot /// /// An error will be returned on failure. - pub fn decrypt_master_key(&self, password: &Protected>) -> Result>> { + #[allow(clippy::needless_pass_by_value)] + pub fn decrypt_master_key(&self, password: Protected>) -> Result>> { let key = self .hashing_algorithm - .hash(password.clone(), self.content_salt) + .hash(password, self.content_salt) .map_err(|_| Error::PasswordHash)?; let derived_key = derive_key(key, self.salt, FILE_KEY_CONTEXT); @@ -141,20 +143,22 @@ impl Keyslot { /// This function is used to serialize a keyslot into bytes #[must_use] - pub fn serialize(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { match self.version { - KeyslotVersion::V1 => { - let mut keyslot = Vec::new(); - keyslot.extend_from_slice(&self.version.serialize()); // 2 - keyslot.extend_from_slice(&self.algorithm.serialize()); // 4 - keyslot.extend_from_slice(&self.hashing_algorithm.serialize()); // 6 - keyslot.extend_from_slice(&self.salt); // 22 - keyslot.extend_from_slice(&self.content_salt); // 38 - keyslot.extend_from_slice(&self.master_key); // 86 - keyslot.extend_from_slice(&self.nonce); // 94 or 106 - keyslot.extend_from_slice(&vec![0u8; 26 - self.nonce.len()]); // 112 total bytes - keyslot - } + KeyslotVersion::V1 => vec![ + self.version.to_bytes().as_ref(), + self.algorithm.to_bytes().as_ref(), + self.hashing_algorithm.to_bytes().as_ref(), + &self.salt, + &self.content_salt, + &self.master_key, + &self.nonce, + &vec![0u8; 26 - self.nonce.len()], + ] + .iter() + .flat_map(|&v| v) + .copied() + .collect(), } } @@ -163,23 +167,23 @@ impl Keyslot { /// It will leave the cursor at the end of the keyslot on success /// /// The cursor will not be rewound on error. - pub fn deserialize(reader: &mut R) -> Result + pub fn from_reader(reader: &mut R) -> Result where - R: Read + Seek, + R: Read, { let mut version = [0u8; 2]; reader.read_exact(&mut version)?; - let version = KeyslotVersion::deserialize(version)?; + let version = KeyslotVersion::from_bytes(version)?; match version { KeyslotVersion::V1 => { let mut algorithm = [0u8; 2]; reader.read_exact(&mut algorithm)?; - let algorithm = Algorithm::deserialize(algorithm)?; + let algorithm = Algorithm::from_bytes(algorithm)?; let mut hashing_algorithm = [0u8; 2]; reader.read_exact(&mut hashing_algorithm)?; - let hashing_algorithm = HashingAlgorithm::deserialize(hashing_algorithm)?; + let hashing_algorithm = HashingAlgorithm::from_bytes(hashing_algorithm)?; let mut salt = [0u8; SALT_LEN]; reader.read_exact(&mut salt)?; diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index fbe206194..6cfa85462 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -27,7 +27,7 @@ //! ) //! .unwrap(); //! ``` -use std::io::{Read, Seek}; +use std::io::Read; #[cfg(feature = "serde")] use crate::{ @@ -67,11 +67,12 @@ impl FileHeader { /// /// Metadata needs to be accessed switfly, so a key management system should handle the salt generation. #[cfg(feature = "serde")] + #[allow(clippy::needless_pass_by_value)] pub fn add_metadata( &mut self, version: MetadataVersion, algorithm: Algorithm, - master_key: &Protected<[u8; KEY_LEN]>, + master_key: Protected<[u8; KEY_LEN]>, metadata: &T, ) -> Result<()> where @@ -80,10 +81,10 @@ impl FileHeader { let metadata_nonce = generate_nonce(algorithm); let encrypted_metadata = StreamEncryption::encrypt_bytes( - master_key.clone(), + master_key, &metadata_nonce, algorithm, - &serde_json::to_vec(metadata).map_err(|_| Error::MetadataDeSerialization)?, + &serde_json::to_vec(metadata).map_err(|_| Error::Serialization)?, &[], )?; @@ -124,7 +125,7 @@ impl FileHeader { &[], )?; - serde_json::from_slice::(&metadata).map_err(|_| Error::MetadataDeSerialization) + serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) } else { Err(Error::NoMetadata) } @@ -152,7 +153,7 @@ impl FileHeader { &[], )?; - serde_json::from_slice::(&metadata).map_err(|_| Error::MetadataDeSerialization) + serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) } else { Err(Error::NoMetadata) } @@ -162,28 +163,27 @@ impl FileHeader { impl Metadata { #[must_use] pub fn size(&self) -> usize { - self.serialize().len() + self.to_bytes().len() } /// This function is used to serialize a metadata item into bytes /// /// This also includes the encrypted metadata itself, so this may be sizeable #[must_use] - pub fn serialize(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { match self.version { - MetadataVersion::V1 => { - let mut metadata = Vec::new(); - metadata.extend_from_slice(&self.version.serialize()); // 2 - metadata.extend_from_slice(&self.algorithm.serialize()); // 4 - metadata.extend_from_slice(&self.metadata_nonce); // 24 max - metadata.extend_from_slice(&vec![0u8; 24 - self.metadata_nonce.len()]); // 28 - - let metadata_len = self.metadata.len() as u64; - - metadata.extend_from_slice(&metadata_len.to_le_bytes()); // 36 total bytes - metadata.extend_from_slice(&self.metadata); // this can vary in length - metadata - } + MetadataVersion::V1 => vec![ + self.version.to_bytes().as_ref(), + self.algorithm.to_bytes().as_ref(), + &self.metadata_nonce, + &vec![0u8; 24 - self.metadata_nonce.len()], + &(self.metadata.len() as u64).to_le_bytes(), + &self.metadata, + ] + .iter() + .flat_map(|&v| v) + .copied() + .collect(), } } @@ -192,19 +192,19 @@ impl Metadata { /// The cursor will be left at the end of the metadata item on success /// /// The cursor will not be rewound on error. - pub fn deserialize(reader: &mut R) -> Result + pub fn from_reader(reader: &mut R) -> Result where - R: Read + Seek, + R: Read, { let mut version = [0u8; 2]; reader.read_exact(&mut version)?; - let version = MetadataVersion::deserialize(version).map_err(|_| Error::NoMetadata)?; + let version = MetadataVersion::from_bytes(version).map_err(|_| Error::NoMetadata)?; match version { MetadataVersion::V1 => { let mut algorithm = [0u8; 2]; reader.read_exact(&mut algorithm)?; - let algorithm = Algorithm::deserialize(algorithm)?; + let algorithm = Algorithm::from_bytes(algorithm)?; let mut metadata_nonce = vec![0u8; algorithm.nonce_len()]; reader.read_exact(&mut metadata_nonce)?; diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index 31f3aa0a1..3b38f9d6e 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -20,7 +20,7 @@ //! ) //! .unwrap(); //! ``` -use std::io::{Read, Seek}; +use std::io::Read; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, @@ -56,22 +56,18 @@ impl FileHeader { /// You will need to provide the user's password, and a semi-universal salt for hashing the user's password. This allows for extremely fast decryption. /// /// Preview media needs to be accessed switfly, so a key management system should handle the salt generation. + #[allow(clippy::needless_pass_by_value)] pub fn add_preview_media( &mut self, version: PreviewMediaVersion, algorithm: Algorithm, - master_key: &Protected<[u8; KEY_LEN]>, + master_key: Protected<[u8; KEY_LEN]>, media: &[u8], ) -> Result<()> { let media_nonce = generate_nonce(algorithm); - let encrypted_media = StreamEncryption::encrypt_bytes( - master_key.clone(), - &media_nonce, - algorithm, - media, - &[], - )?; + let encrypted_media = + StreamEncryption::encrypt_bytes(master_key, &media_nonce, algorithm, media, &[])?; let pvm = PreviewMedia { version, @@ -143,28 +139,27 @@ impl FileHeader { impl PreviewMedia { #[must_use] pub fn size(&self) -> usize { - self.serialize().len() + self.to_bytes().len() } /// This function is used to serialize a preview media header item into bytes /// /// This also includes the encrypted preview media itself, so this may be sizeable #[must_use] - pub fn serialize(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { match self.version { - PreviewMediaVersion::V1 => { - let mut preview_media = Vec::new(); - preview_media.extend_from_slice(&self.version.serialize()); // 2 - preview_media.extend_from_slice(&self.algorithm.serialize()); // 4 - preview_media.extend_from_slice(&self.media_nonce); // 24 max - preview_media.extend_from_slice(&vec![0u8; 24 - self.media_nonce.len()]); // 28 total bytes - - let media_len = self.media.len() as u64; - - preview_media.extend_from_slice(&media_len.to_le_bytes()); // 36 total bytes - preview_media.extend_from_slice(&self.media); // this can vary in length - preview_media - } + PreviewMediaVersion::V1 => vec![ + self.version.to_bytes().as_ref(), + self.algorithm.to_bytes().as_ref(), + &self.media_nonce, + &vec![0u8; 24 - self.media_nonce.len()], + &(self.media.len() as u64).to_le_bytes(), + &self.media, + ] + .iter() + .flat_map(|&v| v) + .copied() + .collect(), } } @@ -173,20 +168,20 @@ impl PreviewMedia { /// The cursor will be left at the end of the preview media item on success /// /// The cursor will not be rewound on error. - pub fn deserialize(reader: &mut R) -> Result + pub fn from_reader(reader: &mut R) -> Result where - R: Read + Seek, + R: Read, { let mut version = [0u8; 2]; reader.read_exact(&mut version)?; let version = - PreviewMediaVersion::deserialize(version).map_err(|_| Error::NoPreviewMedia)?; + PreviewMediaVersion::from_bytes(version).map_err(|_| Error::NoPreviewMedia)?; match version { PreviewMediaVersion::V1 => { let mut algorithm = [0u8; 2]; reader.read_exact(&mut algorithm)?; - let algorithm = Algorithm::deserialize(algorithm)?; + let algorithm = Algorithm::from_bytes(algorithm)?; let mut media_nonce = vec![0u8; algorithm.nonce_len()]; reader.read_exact(&mut media_nonce)?; diff --git a/crates/crypto/src/header/serialization.rs b/crates/crypto/src/header/serialization.rs index a4c49a34d..46a27851b 100644 --- a/crates/crypto/src/header/serialization.rs +++ b/crates/crypto/src/header/serialization.rs @@ -16,16 +16,16 @@ use super::{ impl FileHeaderVersion { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::V1 => [0x0A, 0x01], } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { [0x0A, 0x01] => Ok(Self::V1), - _ => Err(Error::FileHeader), + _ => Err(Error::Serialization), } } } @@ -40,16 +40,16 @@ impl Display for FileHeaderVersion { impl KeyslotVersion { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::V1 => [0x0D, 0x01], } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { [0x0D, 0x01] => Ok(Self::V1), - _ => Err(Error::FileHeader), + _ => Err(Error::Serialization), } } } @@ -64,16 +64,16 @@ impl Display for KeyslotVersion { impl PreviewMediaVersion { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::V1 => [0x0E, 0x01], } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { [0x0E, 0x01] => Ok(Self::V1), - _ => Err(Error::FileHeader), + _ => Err(Error::Serialization), } } } @@ -88,16 +88,16 @@ impl Display for PreviewMediaVersion { impl MetadataVersion { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::V1 => [0x1F, 0x01], } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { [0x1F, 0x01] => Ok(Self::V1), - _ => Err(Error::FileHeader), + _ => Err(Error::Serialization), } } } @@ -112,22 +112,30 @@ impl Display for MetadataVersion { impl HashingAlgorithm { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::Argon2id(p) => match p { - Params::Standard => [0x0F, 0x01], - Params::Hardened => [0x0F, 0x02], - Params::Paranoid => [0x0F, 0x03], + Params::Standard => [0xA2, 0x01], + Params::Hardened => [0xA2, 0x02], + Params::Paranoid => [0xA2, 0x03], + }, + Self::BalloonBlake3(p) => match p { + Params::Standard => [0xB3, 0x01], + Params::Hardened => [0xB3, 0x02], + Params::Paranoid => [0xB3, 0x03], }, } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { - [0x0F, 0x01] => Ok(Self::Argon2id(Params::Standard)), - [0x0F, 0x02] => Ok(Self::Argon2id(Params::Hardened)), - [0x0F, 0x03] => Ok(Self::Argon2id(Params::Paranoid)), - _ => Err(Error::FileHeader), + [0xA2, 0x01] => Ok(Self::Argon2id(Params::Standard)), + [0xA2, 0x02] => Ok(Self::Argon2id(Params::Hardened)), + [0xA2, 0x03] => Ok(Self::Argon2id(Params::Paranoid)), + [0xB3, 0x01] => Ok(Self::BalloonBlake3(Params::Standard)), + [0xB3, 0x02] => Ok(Self::BalloonBlake3(Params::Hardened)), + [0xB3, 0x03] => Ok(Self::BalloonBlake3(Params::Paranoid)), + _ => Err(Error::Serialization), } } } @@ -136,6 +144,7 @@ impl Display for HashingAlgorithm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { Self::Argon2id(p) => write!(f, "Argon2id ({})", p), + Self::BalloonBlake3(p) => write!(f, "BLAKE3-Balloon ({})", p), } } } @@ -152,18 +161,18 @@ impl Display for Params { impl Algorithm { #[must_use] - pub const fn serialize(&self) -> [u8; 2] { + pub const fn to_bytes(&self) -> [u8; 2] { match self { Self::XChaCha20Poly1305 => [0x0B, 0x01], Self::Aes256Gcm => [0x0B, 0x02], } } - pub const fn deserialize(bytes: [u8; 2]) -> Result { + pub const fn from_bytes(bytes: [u8; 2]) -> Result { match bytes { [0x0B, 0x01] => Ok(Self::XChaCha20Poly1305), [0x0B, 0x02] => Ok(Self::Aes256Gcm), - _ => Err(Error::FileHeader), + _ => Err(Error::Serialization), } } } diff --git a/crates/crypto/src/keys/hashing.rs b/crates/crypto/src/keys/hashing.rs index 198d3ed0c..1477ed0b9 100644 --- a/crates/crypto/src/keys/hashing.rs +++ b/crates/crypto/src/keys/hashing.rs @@ -15,6 +15,7 @@ use crate::primitives::KEY_LEN; use crate::Protected; use crate::{primitives::SALT_LEN, Error, Result}; use argon2::Argon2; +use balloon_hash::Balloon; /// These parameters define the password-hashing level. /// @@ -42,6 +43,7 @@ pub enum Params { #[cfg_attr(feature = "rspc", derive(specta::Type))] pub enum HashingAlgorithm { Argon2id(Params), + BalloonBlake3(Params), } impl HashingAlgorithm { @@ -54,7 +56,8 @@ impl HashingAlgorithm { salt: [u8; SALT_LEN], ) -> Result> { match self { - Self::Argon2id(params) => password_hash_argon2id(password, salt, *params), + Self::Argon2id(params) => PasswordHasher::argon2id(password, salt, *params), + Self::BalloonBlake3(params) => PasswordHasher::balloon_blake3(password, salt, *params), } } } @@ -64,51 +67,75 @@ impl Params { /// /// This should not be called directly. Call it via the `HashingAlgorithm` struct (e.g. `HashingAlgorithm::Argon2id(Params::Standard).hash()`) #[must_use] - pub fn get_argon2_params(&self) -> argon2::Params { + pub fn argon2id(&self) -> argon2::Params { match self { // We can use `.unwrap()` here as the values are hardcoded, and this shouldn't error // The values are NOT final, as we need to find a good average. // It's very hardware dependant but we should aim for at least 64MB of RAM usage on standard // Provided they all take one (ish) second or longer, and less than 3/4 seconds (for paranoid), they will be fine // It's not so much the parameters themselves that matter, it's the duration (and ensuring that they use enough RAM to hinder ASIC brute-force attacks) - Self::Standard => { - argon2::Params::new(131_072, 8, 4, Some(argon2::Params::DEFAULT_OUTPUT_LEN)) - .unwrap() - } - Self::Paranoid => { - argon2::Params::new(262_144, 8, 4, Some(argon2::Params::DEFAULT_OUTPUT_LEN)) - .unwrap() - } - Self::Hardened => { - argon2::Params::new(524_288, 8, 4, Some(argon2::Params::DEFAULT_OUTPUT_LEN)) - .unwrap() - } + Self::Standard => argon2::Params::new(131_072, 8, 4, None).unwrap(), + Self::Paranoid => argon2::Params::new(262_144, 8, 4, None).unwrap(), + Self::Hardened => argon2::Params::new(524_288, 8, 4, None).unwrap(), + } + } + + /// This function is used to generate parameters for password hashing. + /// + /// This should not be called directly. Call it via the `HashingAlgorithm` struct (e.g. `HashingAlgorithm::Argon2id(Params::Standard).hash()`) + #[must_use] + pub fn balloon_blake3(&self) -> balloon_hash::Params { + match self { + // We can use `.unwrap()` here as the values are hardcoded, and this shouldn't error + // The values are NOT final, as we need to find a good average. + // It's very hardware dependant but we should aim for at least 64MB of RAM usage on standard + // Provided they all take one (ish) second or longer, and less than 3/4 seconds (for paranoid), they will be fine + // It's not so much the parameters themselves that matter, it's the duration (and ensuring that they use enough RAM to hinder ASIC brute-force attacks) + Self::Standard => balloon_hash::Params::new(131_072, 1, 1).unwrap(), + Self::Paranoid => balloon_hash::Params::new(262_144, 1, 1).unwrap(), + Self::Hardened => balloon_hash::Params::new(524_288, 1, 1).unwrap(), } } } -/// This function should NOT be called directly! -/// -/// Call it via the `HashingAlgorithm` struct (e.g. `HashingAlgorithm::Argon2id(Params::Standard).hash()`) -#[allow(clippy::needless_pass_by_value)] -pub fn password_hash_argon2id( - password: Protected>, - salt: [u8; SALT_LEN], - params: Params, -) -> Result> { - let mut key = [0u8; KEY_LEN]; +struct PasswordHasher; - let argon2 = Argon2::new( - argon2::Algorithm::Argon2id, - argon2::Version::V0x13, - params.get_argon2_params(), - ); +impl PasswordHasher { + #[allow(clippy::needless_pass_by_value)] + fn argon2id( + password: Protected>, + salt: [u8; SALT_LEN], + params: Params, + ) -> Result> { + let mut key = [0u8; KEY_LEN]; - let result = argon2.hash_password_into(password.expose(), &salt, &mut key); + let argon2 = Argon2::new( + argon2::Algorithm::Argon2id, + argon2::Version::V0x13, + params.argon2id(), + ); - if result.is_ok() { - Ok(Protected::new(key)) - } else { - Err(Error::PasswordHash) + argon2 + .hash_password_into(password.expose(), &salt, &mut key) + .map_or(Err(Error::PasswordHash), |_| Ok(Protected::new(key))) + } + + #[allow(clippy::needless_pass_by_value)] + fn balloon_blake3( + password: Protected>, + salt: [u8; SALT_LEN], + params: Params, + ) -> Result> { + let mut key = [0u8; KEY_LEN]; + + let balloon = Balloon::::new( + balloon_hash::Algorithm::Balloon, + params.balloon_blake3(), + None, + ); + + balloon + .hash_into(password.expose(), &salt, &mut key) + .map_or(Err(Error::PasswordHash), |_| Ok(Protected::new(key))) } } diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index d16379083..3ac25baea 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -105,7 +105,7 @@ export interface GenerateThumbsForLocationArgs { id: number, path: string } export interface GetArgs { id: number } -export type HashingAlgorithm = { Argon2id: Params } +export type HashingAlgorithm = { Argon2id: Params } | { BalloonBlake3: Params } export interface IdentifyUniqueFilesArgs { id: number, path: string } diff --git a/packages/interface/src/components/dialog/EncryptFileDialog.tsx b/packages/interface/src/components/dialog/EncryptFileDialog.tsx index 1557d828c..8fd4640ff 100644 --- a/packages/interface/src/components/dialog/EncryptFileDialog.tsx +++ b/packages/interface/src/components/dialog/EncryptFileDialog.tsx @@ -159,13 +159,16 @@ export const EncryptFileDialog = (props: EncryptDialogProps) => { Hashing diff --git a/packages/interface/src/components/dialog/KeyViewerDialog.tsx b/packages/interface/src/components/dialog/KeyViewerDialog.tsx index eeaeea483..c9d16c399 100644 --- a/packages/interface/src/components/dialog/KeyViewerDialog.tsx +++ b/packages/interface/src/components/dialog/KeyViewerDialog.tsx @@ -105,6 +105,9 @@ export const KeyViewerDialog = (props: KeyViewerDialogProps) => { Argon2id (standard) Argon2id (hardened) Argon2id (paranoid) + Blake3-Balloon (standard) + Blake3-Balloon (hardened) + Blake3-Balloon (paranoid) diff --git a/packages/interface/src/components/key/KeyList.tsx b/packages/interface/src/components/key/KeyList.tsx index 992102fda..7693c66cf 100644 --- a/packages/interface/src/components/key/KeyList.tsx +++ b/packages/interface/src/components/key/KeyList.tsx @@ -42,6 +42,7 @@ export const ListOfKeys = () => { return ( { - if (autoMount && e) setAutoMount(false); + if (autoMount && !e) setAutoMount(false); setLibrarySync(e); }} /> @@ -106,7 +106,7 @@ export function KeyMounter() { size="sm" checked={autoMount} onCheckedChange={(e) => { - if (librarySync && e) setLibrarySync(false); + if (!librarySync && e) setLibrarySync(true); setAutoMount(e); }} /> @@ -131,6 +131,9 @@ export function KeyMounter() { Argon2id (standard) Argon2id (hardened) Argon2id (paranoid) + Blake3-Balloon (standard) + Blake3-Balloon (hardened) + Blake3-Balloon (paranoid) diff --git a/packages/interface/src/screens/settings/library/KeysSetting.tsx b/packages/interface/src/screens/settings/library/KeysSetting.tsx index 8fcbd561f..3272a931d 100644 --- a/packages/interface/src/screens/settings/library/KeysSetting.tsx +++ b/packages/interface/src/screens/settings/library/KeysSetting.tsx @@ -304,6 +304,15 @@ export const getCryptoSettings = ( case 'Argon2id-p': hashing_algorithm = { Argon2id: 'Paranoid' as Params }; break; + case 'BalloonBlake3-s': + hashing_algorithm = { BalloonBlake3: 'Standard' as Params }; + break; + case 'BalloonBlake3-h': + hashing_algorithm = { BalloonBlake3: 'Hardened' as Params }; + break; + case 'BalloonBlake3-p': + hashing_algorithm = { BalloonBlake3: 'Paranoid' as Params }; + break; } return [algorithm, hashing_algorithm]; @@ -313,16 +322,25 @@ export const getCryptoSettings = ( export const getHashingAlgorithmString = (hashingAlgorithm: HashingAlgorithm): string => { let hashing_algorithm = ''; - switch (hashingAlgorithm.Argon2id) { - case 'Standard': + switch (hashingAlgorithm) { + case { Argon2id: 'Standard' }: hashing_algorithm = 'Argon2id-s'; break; - case 'Hardened': + case { Argon2id: 'Hardened' }: hashing_algorithm = 'Argon2id-h'; break; - case 'Paranoid': + case { Argon2id: 'Paranoid' }: hashing_algorithm = 'Argon2id-p'; break; + case { BalloonBlake3: 'Standard' }: + hashing_algorithm = 'BalloonBlake3-s'; + break; + case { BalloonBlake3: 'Hardened' }: + hashing_algorithm = 'BalloonBlake3-h'; + break; + case { BalloonBlake3: 'Paranoid' }: + hashing_algorithm = 'BalloonBlake3-p'; + break; } return hashing_algorithm;