Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions crates/core/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
error::RusticResult,
id::Id,
repofile::{ConfigFile, KeyId, configfile::RepositoryId},
repository::Repository,
repository::{Repository, credentials::Credentials},
};

/// Initialize a new repository.
Expand All @@ -26,7 +26,7 @@ use crate::{
/// # Arguments
///
/// * `repo` - The repository to initialize.
/// * `pass` - The password to encrypt the key with.
/// * `credentials` - The credentials to use.
/// * `key_opts` - The options to create the key with.
/// * `config_opts` - The options to create the config with.
///
Expand All @@ -39,22 +39,22 @@ use crate::{
/// A tuple of the key and the config file.
pub(crate) fn init<P, S>(
repo: &Repository<P, S>,
pass: &str,
credentials: &Credentials,
key_opts: &KeyOptions,
config_opts: &ConfigOptions,
) -> RusticResult<(Key, KeyId, ConfigFile)> {
) -> RusticResult<(Key, Option<KeyId>, ConfigFile)> {
// Create config first to allow catching errors from here without writing anything
let repo_id = RepositoryId::from(Id::random());
let chunker_poly = random_poly()?;
let mut config = ConfigFile::new(2, repo_id, chunker_poly);
if repo.be_hot.is_some() {
// for hot/cold repository, `config` must be identical to thee config file which is read by the backend, i.e. the one saved in the hot repo.
// for hot/cold repository, `config` must be identical to the config file which is read by the backend, i.e. the one saved in the hot repo.
// Note: init_with_config does handle the is_hot config correctly for the hot and the cold repo.
config.is_hot = Some(true);
}
config_opts.apply(&mut config)?;

let (key, key_id) = init_with_config(repo, pass, key_opts, &config)?;
let (key, key_id) = init_with_config(repo, credentials, key_opts, &config)?;
info!("repository {repo_id} successfully created.");

Ok((key, key_id, config))
Expand All @@ -79,13 +79,19 @@ pub(crate) fn init<P, S>(
/// The key used to encrypt the config.
pub(crate) fn init_with_config<P, S>(
repo: &Repository<P, S>,
pass: &str,
credentials: &Credentials,
key_opts: &KeyOptions,
config: &ConfigFile,
) -> RusticResult<(Key, KeyId)> {
) -> RusticResult<(Key, Option<KeyId>)> {
repo.be.create()?;
let (key, id) = init_key(repo, key_opts, pass)?;
info!("key {id} successfully added.");
let (key, id) = match credentials {
Credentials::Password(password) => {
let (key, id) = init_key(repo, key_opts, password)?;
info!("key {id} successfully added.");
(key, Some(id))
}
Credentials::Masterkey(key) => (key.key(), None),
};
save_config(repo, config.clone(), key)?;

Ok((key, id))
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ pub enum ErrorKind {
/// general operations
#[default]
Other,
/// password handling
Password,
/// credentials handling
Credentials,
/// the repository
Repository,
/// unsupported operations
Expand Down
17 changes: 11 additions & 6 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ implement [`serde::Serialize`] and [`serde::Deserialize`].

```rust
use rustic_backend::BackendOptions;
use rustic_core::{BackupOptions, ConfigOptions, KeyOptions, PathList,
Repository, RepositoryOptions, SnapshotOptions
use rustic_core::{repofile::MasterKey, BackupOptions, ConfigOptions, Credentials,
KeyOptions, PathList, Repository, RepositoryOptions, SnapshotOptions,
};

// Initialize the repository in a temporary dir
let repo_dir = tempfile::tempdir().unwrap();

let repo_opts = RepositoryOptions::default()
.password("test");
let repo_opts = RepositoryOptions::default();
// In real life, make sure to save this credential!
let credentials = Credentials::Masterkey(MasterKey::new());

// Initialize Backends
let backends = BackendOptions::default()
Expand All @@ -45,10 +46,13 @@ implement [`serde::Serialize`] and [`serde::Deserialize`].

let config_opts = ConfigOptions::default();

let _repo = Repository::new(&repo_opts, &backends.clone()).unwrap().init(&key_opts, &config_opts).unwrap();
let _repo = Repository::new(&repo_opts, &backends)
.unwrap()
.init(&credentials, &key_opts, &config_opts)
.unwrap();

// We could have used _repo directly, but open the repository again to show how to open it...
let repo = Repository::new(&repo_opts, &backends).unwrap().open().unwrap();
let repo = Repository::new(&repo_opts, &backends).unwrap().open(&credentials).unwrap();

// Get all snapshots from the repository
let snaps = repo.get_all_snapshots().unwrap();
Expand Down Expand Up @@ -165,5 +169,6 @@ pub use crate::{
FullIndex, IdIndex, IndexedFull, IndexedIds, IndexedStatus, IndexedTree, Open, OpenStatus,
Repository, RepositoryOptions,
command_input::{CommandInput, CommandInputErrorKind},
credentials::{CredentialOptions, Credentials},
},
};
2 changes: 1 addition & 1 deletion crates/core/src/repofile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ pub use {
},
configfile::{Chunker, ConfigFile},
indexfile::{IndexBlob, IndexFile, IndexId, IndexPack},
keyfile::{KeyFile, KeyId},
keyfile::{KeyFile, KeyId, MasterKey},
packfile::{HeaderEntry, PackHeader, PackHeaderLength, PackHeaderRef, PackId},
snapshotfile::{
DeleteOption, PathList, SnapshotFile, SnapshotId, SnapshotModification, SnapshotSummary,
Expand Down
34 changes: 23 additions & 11 deletions crates/core/src/repofile/keyfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,31 +306,43 @@ fn log_2(x: u32) -> KeyFileResult<u8> {
///
/// This is used to verify the integrity of the key
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct Mac {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Mac {
/// The key used for the mac
#[serde_as(as = "Base64")]
k: Vec<u8>,
pub k: Vec<u8>,

/// The random value used for the mac
#[serde_as(as = "Base64")]
r: Vec<u8>,
pub r: Vec<u8>,
}

/// The master key of a [`Key`]
///
/// This is used to encrypt the key
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct MasterKey {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MasterKey {
/// The mac of the key
mac: Mac,
pub mac: Mac,
/// The encrypted key
#[serde_as(as = "Base64")]
encrypt: Vec<u8>,
pub encrypt: Vec<u8>,
}

impl Default for MasterKey {
fn default() -> Self {
Self::from_key(Key::new())
}
}

impl MasterKey {
/// Create a random [`MasterKey`]
#[must_use]
pub fn new() -> Self {
Self::default()
}

/// Create a [`MasterKey`] from a [`Key`]
///
/// # Arguments
Expand All @@ -340,7 +352,7 @@ impl MasterKey {
/// # Returns
///
/// The created [`MasterKey`]
fn from_key(key: Key) -> Self {
pub(crate) fn from_key(key: Key) -> Self {
let (encrypt, k, r) = key.to_keys();
Self {
encrypt,
Expand All @@ -349,7 +361,7 @@ impl MasterKey {
}

/// Get the [`Key`] from the [`MasterKey`]
fn key(&self) -> Key {
pub(crate) fn key(&self) -> Key {
Key::from_keys(&self.encrypt, &self.mac.k, &self.mac.r)
}
}
Expand Down Expand Up @@ -407,7 +419,7 @@ pub(crate) fn find_key_in_backend<B: ReadBackend>(
}

Err(RusticError::new(
ErrorKind::Password,
ErrorKind::Credentials,
"The password that has been entered, seems to be incorrect. No suitable key found for the given password. Please check your password and try again.",
).attach_error_code("C002"))
}
Expand Down
Loading
Loading