mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-04-19 22:19:49 -04:00
async crypto!
This commit is contained in:
@@ -23,7 +23,7 @@ pub struct KeyAddArgs {
|
||||
}
|
||||
|
||||
#[derive(Type, Deserialize)]
|
||||
pub struct SetMasterPasswordArgs {
|
||||
pub struct UnlockKeyManagerArgs {
|
||||
password: String,
|
||||
secret_key: Option<String>,
|
||||
}
|
||||
@@ -64,14 +64,14 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
})
|
||||
.library_query("getKey", |t| {
|
||||
t(|_, key_uuid: Uuid, library| async move {
|
||||
let key = library.key_manager.get_key(key_uuid)?;
|
||||
let key = library.key_manager.get_key(key_uuid).await?;
|
||||
|
||||
Ok(String::from_utf8(key.into_inner()).map_err(Error::StringParse)?)
|
||||
})
|
||||
})
|
||||
.library_mutation("mount", |t| {
|
||||
t(|_, key_uuid: Uuid, library| async move {
|
||||
library.key_manager.mount(key_uuid)?;
|
||||
library.key_manager.mount(key_uuid).await?;
|
||||
// we also need to dispatch jobs that automatically decrypt preview media and metadata here
|
||||
invalidate_query!(library, "keys.listMounted");
|
||||
Ok(())
|
||||
@@ -148,14 +148,17 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.library_mutation("setMasterPassword", |t| {
|
||||
t(|_, args: SetMasterPasswordArgs, library| async move {
|
||||
.library_mutation("unlockKeyManager", |t| {
|
||||
t(|_, args: UnlockKeyManagerArgs, library| async move {
|
||||
// if this returns an error, the user MUST re-enter the correct password
|
||||
library.key_manager.set_master_password(
|
||||
Protected::new(args.password),
|
||||
args.secret_key.map(Protected::new),
|
||||
|| invalidate_query!(library, "keys.isKeyManagerUnlocking"),
|
||||
)?;
|
||||
library
|
||||
.key_manager
|
||||
.unlock(
|
||||
Protected::new(args.password),
|
||||
args.secret_key.map(Protected::new),
|
||||
|| invalidate_query!(library, "keys.isKeyManagerUnlocking"),
|
||||
)
|
||||
.await?;
|
||||
|
||||
invalidate_query!(library, "keys.hasMasterPassword");
|
||||
|
||||
@@ -169,7 +172,8 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
for key in automount {
|
||||
library
|
||||
.key_manager
|
||||
.mount(Uuid::from_str(&key.uuid).map_err(|_| Error::Serialization)?)?;
|
||||
.mount(Uuid::from_str(&key.uuid).map_err(|_| Error::Serialization)?)
|
||||
.await?;
|
||||
|
||||
invalidate_query!(library, "keys.listMounted");
|
||||
}
|
||||
@@ -222,14 +226,17 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
.library_mutation("add", |t| {
|
||||
t(|_, args: KeyAddArgs, library| async move {
|
||||
// register the key with the keymanager
|
||||
let uuid = library.key_manager.add_to_keystore(
|
||||
Protected::new(args.key.as_bytes().to_vec()),
|
||||
args.algorithm,
|
||||
args.hashing_algorithm,
|
||||
!args.library_sync,
|
||||
args.automount,
|
||||
None,
|
||||
)?;
|
||||
let uuid = library
|
||||
.key_manager
|
||||
.add_to_keystore(
|
||||
Protected::new(args.key.as_bytes().to_vec()),
|
||||
args.algorithm,
|
||||
args.hashing_algorithm,
|
||||
!args.library_sync,
|
||||
args.automount,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if args.library_sync {
|
||||
write_storedkey_to_db(&library.db, &library.key_manager.access_keystore(uuid)?)
|
||||
@@ -248,7 +255,7 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
library.key_manager.mount(uuid)?;
|
||||
library.key_manager.mount(uuid).await?;
|
||||
|
||||
invalidate_query!(library, "keys.list");
|
||||
invalidate_query!(library, "keys.listMounted");
|
||||
@@ -290,11 +297,10 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
|
||||
let secret_key = args.secret_key.map(Protected::new);
|
||||
|
||||
let updated_keys = library.key_manager.import_keystore_backup(
|
||||
Protected::new(args.password),
|
||||
secret_key,
|
||||
&stored_keys,
|
||||
)?;
|
||||
let updated_keys = library
|
||||
.key_manager
|
||||
.import_keystore_backup(Protected::new(args.password), secret_key, &stored_keys)
|
||||
.await?;
|
||||
|
||||
for key in &updated_keys {
|
||||
write_storedkey_to_db(&library.db, key).await?;
|
||||
@@ -310,12 +316,15 @@ pub(crate) fn mount() -> RouterBuilder {
|
||||
t(|_, args: MasterPasswordChangeArgs, library| async move {
|
||||
let secret_key = args.secret_key.map(Protected::new);
|
||||
|
||||
let verification_key = library.key_manager.change_master_password(
|
||||
Protected::new(args.password),
|
||||
args.algorithm,
|
||||
args.hashing_algorithm,
|
||||
secret_key,
|
||||
)?;
|
||||
let verification_key = library
|
||||
.key_manager
|
||||
.change_master_password(
|
||||
Protected::new(args.password),
|
||||
args.algorithm,
|
||||
args.hashing_algorithm,
|
||||
secret_key,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// remove old nil-id keys if they were set
|
||||
library
|
||||
|
||||
@@ -197,7 +197,7 @@ impl LibraryManager {
|
||||
indexer_rules_seeder(&library.db).await?;
|
||||
|
||||
// setup master password
|
||||
let verification_key = KeyManager::onboarding(km_config)?;
|
||||
let verification_key = KeyManager::onboarding(km_config).await?;
|
||||
|
||||
write_storedkey_to_db(&library.db, &verification_key).await?;
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use sd_crypto::{crypto::stream::StreamDecryption, header::file::FileHeader, Protected};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use std::{collections::VecDeque, fs::File, path::PathBuf};
|
||||
|
||||
use tokio::task;
|
||||
use std::{collections::VecDeque, path::PathBuf};
|
||||
use tokio::fs::File;
|
||||
|
||||
use crate::job::{JobError, JobReportUpdate, JobResult, JobState, StatefulJob, WorkerContext};
|
||||
|
||||
@@ -82,10 +81,10 @@ impl StatefulJob for FileDecryptorJob {
|
||||
|p| p,
|
||||
);
|
||||
|
||||
let mut reader = File::open(info.obj_path.clone())?;
|
||||
let mut writer = File::create(output_path)?;
|
||||
let mut reader = File::open(info.obj_path.clone()).await?;
|
||||
let mut writer = File::create(output_path).await?;
|
||||
|
||||
let (header, aad) = FileHeader::from_reader(&mut reader)?;
|
||||
let (header, aad) = FileHeader::from_reader(&mut reader).await?;
|
||||
|
||||
let master_key = if let Some(password) = state.init.password.clone() {
|
||||
if let Some(save_to_library) = state.init.save_to_library {
|
||||
@@ -93,20 +92,23 @@ impl StatefulJob for FileDecryptorJob {
|
||||
|
||||
// we can do this first, as `find_key_index` requires a successful decryption (just like `decrypt_master_key`)
|
||||
if save_to_library {
|
||||
let index = header.find_key_index(password.clone())?;
|
||||
let index = header.find_key_index(password.clone()).await?;
|
||||
|
||||
// inherit the encryption algorithm from the keyslot
|
||||
ctx.library_ctx.key_manager.add_to_keystore(
|
||||
password.clone(),
|
||||
header.algorithm,
|
||||
header.keyslots[index].hashing_algorithm,
|
||||
false,
|
||||
false,
|
||||
Some(header.keyslots[index].salt),
|
||||
)?;
|
||||
ctx.library_ctx
|
||||
.key_manager
|
||||
.add_to_keystore(
|
||||
password.clone(),
|
||||
header.algorithm,
|
||||
header.keyslots[index].hashing_algorithm,
|
||||
false,
|
||||
false,
|
||||
Some(header.keyslots[index].salt),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
header.decrypt_master_key(password)?
|
||||
header.decrypt_master_key(password).await?
|
||||
} else {
|
||||
return Err(JobError::JobDataNotFound(String::from(
|
||||
"Password decryption selected, but save to library boolean was not included",
|
||||
@@ -115,16 +117,14 @@ impl StatefulJob for FileDecryptorJob {
|
||||
} else {
|
||||
let keys = ctx.library_ctx.key_manager.enumerate_hashed_keys();
|
||||
|
||||
header.decrypt_master_key_from_prehashed(keys)?
|
||||
header.decrypt_master_key_from_prehashed(keys).await?
|
||||
};
|
||||
|
||||
task::block_in_place(|| {
|
||||
let decryptor = StreamDecryption::new(master_key, &header.nonce, header.algorithm)?;
|
||||
let decryptor = StreamDecryption::new(master_key, &header.nonce, header.algorithm)?;
|
||||
|
||||
decryptor.decrypt_streams(&mut reader, &mut writer, &aad)?;
|
||||
|
||||
Ok::<(), JobError>(())
|
||||
})?;
|
||||
decryptor
|
||||
.decrypt_streams(&mut reader, &mut writer, &aad)
|
||||
.await?;
|
||||
|
||||
// need to decrypt preview media/metadata, and maybe add an option in the UI so the user can chosoe to restore these values
|
||||
// for now this can't easily be implemented, as we don't know what the new object id for the file will be (we know the old one, but it may differ)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{collections::VecDeque, fs::File, io::Read, path::PathBuf};
|
||||
use std::{collections::VecDeque, path::PathBuf};
|
||||
|
||||
use tokio::task;
|
||||
use tokio::{fs::File, io::AsyncReadExt};
|
||||
|
||||
use chrono::FixedOffset;
|
||||
use sd_crypto::{
|
||||
@@ -141,22 +141,25 @@ impl StatefulJob for FileEncryptorJob {
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut reader = task::block_in_place(|| File::open(&info.obj_path))?;
|
||||
let mut writer = task::block_in_place(|| File::create(output_path))?;
|
||||
let mut reader = File::open(&info.obj_path).await?;
|
||||
let mut writer = File::create(output_path).await?;
|
||||
|
||||
let master_key = generate_master_key();
|
||||
|
||||
let mut header = FileHeader::new(
|
||||
LATEST_FILE_HEADER,
|
||||
state.init.algorithm,
|
||||
vec![Keyslot::new(
|
||||
LATEST_KEYSLOT,
|
||||
state.init.algorithm,
|
||||
user_key_details.hashing_algorithm,
|
||||
user_key_details.content_salt,
|
||||
user_key,
|
||||
master_key.clone(),
|
||||
)?],
|
||||
vec![
|
||||
Keyslot::new(
|
||||
LATEST_KEYSLOT,
|
||||
state.init.algorithm,
|
||||
user_key_details.hashing_algorithm,
|
||||
user_key_details.content_salt,
|
||||
user_key,
|
||||
master_key.clone(),
|
||||
)
|
||||
.await?,
|
||||
],
|
||||
);
|
||||
|
||||
if state.init.metadata || state.init.preview_media {
|
||||
@@ -187,12 +190,14 @@ impl StatefulJob for FileEncryptorJob {
|
||||
date_modified: object.date_modified,
|
||||
};
|
||||
|
||||
header.add_metadata(
|
||||
LATEST_METADATA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&metadata,
|
||||
)?;
|
||||
header
|
||||
.add_metadata(
|
||||
LATEST_METADATA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&metadata,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// if state.init.preview_media
|
||||
@@ -209,18 +214,17 @@ impl StatefulJob for FileEncryptorJob {
|
||||
|
||||
if tokio::fs::metadata(&pvm_path).await.is_ok() {
|
||||
let mut pvm_bytes = Vec::new();
|
||||
task::block_in_place(|| {
|
||||
let mut pvm_file = File::open(pvm_path)?;
|
||||
pvm_file.read_to_end(&mut pvm_bytes)?;
|
||||
Ok::<_, JobError>(())
|
||||
})?;
|
||||
let mut pvm_file = File::open(pvm_path).await?;
|
||||
pvm_file.read_to_end(&mut pvm_bytes).await?;
|
||||
|
||||
header.add_preview_media(
|
||||
LATEST_PREVIEW_MEDIA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&pvm_bytes,
|
||||
)?;
|
||||
header
|
||||
.add_preview_media(
|
||||
LATEST_PREVIEW_MEDIA,
|
||||
state.init.algorithm,
|
||||
master_key.clone(),
|
||||
&pvm_bytes,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
// should use container encryption if it's a directory
|
||||
@@ -230,15 +234,13 @@ impl StatefulJob for FileEncryptorJob {
|
||||
}
|
||||
}
|
||||
|
||||
task::block_in_place(|| {
|
||||
header.write(&mut writer)?;
|
||||
header.write(&mut writer).await?;
|
||||
|
||||
let encryptor =
|
||||
StreamEncryption::new(master_key, &header.nonce, header.algorithm)?;
|
||||
let encryptor = StreamEncryption::new(master_key, &header.nonce, header.algorithm)?;
|
||||
|
||||
encryptor.encrypt_streams(&mut reader, &mut writer, &header.generate_aad())?;
|
||||
Ok::<_, JobError>(())
|
||||
})?;
|
||||
encryptor
|
||||
.encrypt_streams(&mut reader, &mut writer, &header.generate_aad())
|
||||
.await?;
|
||||
}
|
||||
_ => warn!(
|
||||
"encryption is skipping {} as it isn't a file",
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
//! // Write the header to the file
|
||||
//! header.write(&mut writer).unwrap();
|
||||
//! ```
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::io::SeekFrom;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
||||
|
||||
use crate::{
|
||||
crypto::stream::Algorithm,
|
||||
@@ -121,11 +123,11 @@ impl FileHeader {
|
||||
}
|
||||
|
||||
/// This is a helper function to serialize and write a header to a file.
|
||||
pub fn write<W>(&self, writer: &mut W) -> Result<()>
|
||||
pub async fn write<W>(&self, writer: &mut W) -> Result<()>
|
||||
where
|
||||
W: Write,
|
||||
W: AsyncWriteExt + Unpin + Send,
|
||||
{
|
||||
writer.write_all(&self.to_bytes()?)?;
|
||||
writer.write_all(&self.to_bytes()?).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -254,12 +256,12 @@ 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 from_reader<R>(reader: &mut R) -> Result<(Self, Vec<u8>)>
|
||||
pub async fn from_reader<R>(reader: &mut R) -> Result<(Self, Vec<u8>)>
|
||||
where
|
||||
R: Read + Seek,
|
||||
R: AsyncReadExt + AsyncSeekExt + Unpin + Send,
|
||||
{
|
||||
let mut magic_bytes = [0u8; MAGIC_BYTES.len()];
|
||||
reader.read_exact(&mut magic_bytes)?;
|
||||
reader.read_exact(&mut magic_bytes).await?;
|
||||
|
||||
if magic_bytes != MAGIC_BYTES {
|
||||
return Err(Error::FileHeader);
|
||||
@@ -267,36 +269,38 @@ impl FileHeader {
|
||||
|
||||
let mut version = [0u8; 2];
|
||||
|
||||
reader.read_exact(&mut version)?;
|
||||
reader.read_exact(&mut version).await?;
|
||||
let version = FileHeaderVersion::from_bytes(version)?;
|
||||
|
||||
// Rewind so we can get the AAD
|
||||
reader.rewind()?;
|
||||
reader.rewind().await?;
|
||||
|
||||
// read the aad according to the size
|
||||
let mut aad = vec![0u8; Self::size(version)];
|
||||
reader.read_exact(&mut aad)?;
|
||||
reader.read_exact(&mut aad).await?;
|
||||
|
||||
// seek back to the start (plus magic bytes and the two version bytes)
|
||||
reader.seek(SeekFrom::Start(MAGIC_BYTES.len() as u64 + 2))?;
|
||||
reader
|
||||
.seek(SeekFrom::Start(MAGIC_BYTES.len() as u64 + 2))
|
||||
.await?;
|
||||
|
||||
// read the header
|
||||
let header = match version {
|
||||
FileHeaderVersion::V1 => {
|
||||
let mut algorithm = [0u8; 2];
|
||||
reader.read_exact(&mut algorithm)?;
|
||||
reader.read_exact(&mut algorithm).await?;
|
||||
let algorithm = Algorithm::from_bytes(algorithm)?;
|
||||
|
||||
let mut nonce = vec![0u8; algorithm.nonce_len()];
|
||||
reader.read_exact(&mut nonce)?;
|
||||
reader.read_exact(&mut nonce).await?;
|
||||
|
||||
// read and discard the padding
|
||||
reader.read_exact(&mut vec![0u8; 25 - nonce.len()])?;
|
||||
reader.read_exact(&mut vec![0u8; 25 - nonce.len()]).await?;
|
||||
|
||||
let mut keyslot_bytes = [0u8; (KEYSLOT_SIZE * 2)]; // length of 2x keyslots
|
||||
let mut keyslots: Vec<Keyslot> = Vec::new();
|
||||
|
||||
reader.read_exact(&mut keyslot_bytes)?;
|
||||
reader.read_exact(&mut keyslot_bytes).await?;
|
||||
|
||||
for _ in 0..2 {
|
||||
Keyslot::from_reader(&mut keyslot_bytes.as_ref())
|
||||
@@ -304,18 +308,21 @@ impl FileHeader {
|
||||
.ok();
|
||||
}
|
||||
|
||||
let metadata = Metadata::from_reader(reader).map_or_else(
|
||||
|_| {
|
||||
reader.seek(SeekFrom::Start(
|
||||
let metadata = if let Ok(metadata) = Metadata::from_reader(reader).await {
|
||||
reader
|
||||
.seek(SeekFrom::Start(
|
||||
Self::size(version) as u64 + (KEYSLOT_SIZE * 2) as u64,
|
||||
))?;
|
||||
Ok::<Option<Metadata>, Error>(None)
|
||||
},
|
||||
|metadata| Ok(Some(metadata)),
|
||||
)?;
|
||||
))
|
||||
.await?;
|
||||
Ok::<Option<Metadata>, Error>(Some(metadata))
|
||||
} else {
|
||||
Ok(None)
|
||||
}?;
|
||||
|
||||
let preview_media = PreviewMedia::from_reader(reader).map_or_else(
|
||||
|_| {
|
||||
let preview_media =
|
||||
if let Ok(preview_media) = PreviewMedia::from_reader(reader).await {
|
||||
Ok::<Option<PreviewMedia>, Error>(Some(preview_media))
|
||||
} else {
|
||||
let seek_len = metadata.as_ref().map_or_else(
|
||||
|| Self::size(version) as u64 + (KEYSLOT_SIZE * 2) as u64,
|
||||
|metadata| {
|
||||
@@ -324,12 +331,10 @@ impl FileHeader {
|
||||
},
|
||||
);
|
||||
|
||||
reader.seek(SeekFrom::Start(seek_len))?;
|
||||
reader.seek(SeekFrom::Start(seek_len)).await?;
|
||||
|
||||
Ok::<Option<PreviewMedia>, Error>(None)
|
||||
},
|
||||
|preview_media| Ok(Some(preview_media)),
|
||||
)?;
|
||||
Ok(None)
|
||||
}?;
|
||||
|
||||
Self {
|
||||
version,
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
use std::io::Read;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::{
|
||||
@@ -195,33 +195,35 @@ 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 from_reader<R>(reader: &mut R) -> Result<Self>
|
||||
pub async fn from_reader<R>(reader: &mut R) -> Result<Self>
|
||||
where
|
||||
R: Read,
|
||||
R: AsyncReadExt + Unpin + Send,
|
||||
{
|
||||
let mut version = [0u8; 2];
|
||||
reader.read_exact(&mut version)?;
|
||||
reader.read_exact(&mut version).await?;
|
||||
let version = MetadataVersion::from_bytes(version).map_err(|_| Error::NoMetadata)?;
|
||||
|
||||
match version {
|
||||
MetadataVersion::V1 => {
|
||||
let mut algorithm = [0u8; 2];
|
||||
reader.read_exact(&mut algorithm)?;
|
||||
reader.read_exact(&mut algorithm).await?;
|
||||
let algorithm = Algorithm::from_bytes(algorithm)?;
|
||||
|
||||
let mut metadata_nonce = vec![0u8; algorithm.nonce_len()];
|
||||
reader.read_exact(&mut metadata_nonce)?;
|
||||
reader.read_exact(&mut metadata_nonce).await?;
|
||||
|
||||
reader.read_exact(&mut vec![0u8; 24 - metadata_nonce.len()])?;
|
||||
reader
|
||||
.read_exact(&mut vec![0u8; 24 - metadata_nonce.len()])
|
||||
.await?;
|
||||
|
||||
let mut metadata_length = [0u8; 8];
|
||||
reader.read_exact(&mut metadata_length)?;
|
||||
reader.read_exact(&mut metadata_length).await?;
|
||||
|
||||
let metadata_length = u64::from_le_bytes(metadata_length);
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let mut metadata = vec![0u8; metadata_length as usize];
|
||||
reader.read_exact(&mut metadata)?;
|
||||
reader.read_exact(&mut metadata).await?;
|
||||
|
||||
let metadata = Self {
|
||||
version,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//! ```
|
||||
use std::io::Read;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::{
|
||||
crypto::stream::{Algorithm, StreamDecryption, StreamEncryption},
|
||||
@@ -171,34 +171,36 @@ 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 from_reader<R>(reader: &mut R) -> Result<Self>
|
||||
pub async fn from_reader<R>(reader: &mut R) -> Result<Self>
|
||||
where
|
||||
R: Read,
|
||||
R: AsyncReadExt + Unpin + Send,
|
||||
{
|
||||
let mut version = [0u8; 2];
|
||||
reader.read_exact(&mut version)?;
|
||||
reader.read_exact(&mut version).await?;
|
||||
let version =
|
||||
PreviewMediaVersion::from_bytes(version).map_err(|_| Error::NoPreviewMedia)?;
|
||||
|
||||
match version {
|
||||
PreviewMediaVersion::V1 => {
|
||||
let mut algorithm = [0u8; 2];
|
||||
reader.read_exact(&mut algorithm)?;
|
||||
reader.read_exact(&mut algorithm).await?;
|
||||
let algorithm = Algorithm::from_bytes(algorithm)?;
|
||||
|
||||
let mut media_nonce = vec![0u8; algorithm.nonce_len()];
|
||||
reader.read_exact(&mut media_nonce)?;
|
||||
reader.read_exact(&mut media_nonce).await?;
|
||||
|
||||
reader.read_exact(&mut vec![0u8; 24 - media_nonce.len()])?;
|
||||
reader
|
||||
.read_exact(&mut vec![0u8; 24 - media_nonce.len()])
|
||||
.await?;
|
||||
|
||||
let mut media_length = [0u8; 8];
|
||||
reader.read_exact(&mut media_length)?;
|
||||
reader.read_exact(&mut media_length).await?;
|
||||
|
||||
let media_length = u64::from_le_bytes(media_length);
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let mut media = vec![0u8; media_length as usize];
|
||||
reader.read_exact(&mut media)?;
|
||||
reader.read_exact(&mut media).await?;
|
||||
|
||||
let preview_media = Self {
|
||||
version,
|
||||
|
||||
@@ -425,7 +425,7 @@ impl KeyManager {
|
||||
Ok(reencrypted_keys)
|
||||
}
|
||||
|
||||
/// This requires both the master password and the secret key
|
||||
/// This is used for unlocking the key manager, and requires both the master password and the secret key.
|
||||
///
|
||||
/// The master password and secret key are hashed together.
|
||||
/// This minimises the risk of an attacker obtaining the master password, as both of these are required to unlock the vault (and both should be stored separately).
|
||||
@@ -436,7 +436,7 @@ impl KeyManager {
|
||||
///
|
||||
/// Note: The invalidation function is ran after updating the queue both times, so it isn't required externally.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub async fn set_master_password<F>(
|
||||
pub async fn unlock<F>(
|
||||
&self,
|
||||
master_password: Protected<String>,
|
||||
secret_key: Option<Protected<String>>,
|
||||
|
||||
Reference in New Issue
Block a user