Skip to main content

rustic_core/commands/
key.rs

1//! `key` subcommand
2use derive_setters::Setters;
3
4use crate::{
5    backend::{FileType, WriteBackend, decrypt::DecryptWriteBackend},
6    crypto::{aespoly1305::Key, hasher::hash},
7    error::{ErrorKind, RusticError, RusticResult},
8    repofile::{KeyFile, KeyId},
9    repository::{Open, Repository},
10};
11
12#[cfg_attr(feature = "clap", derive(clap::Parser))]
13#[derive(Debug, Clone, Default, Setters)]
14#[setters(into)]
15#[non_exhaustive]
16/// Options for the `key` command. These are used when creating a new key.
17pub struct KeyOptions {
18    /// Set 'hostname' in public key information
19    #[cfg_attr(feature = "clap", clap(long))]
20    pub hostname: Option<String>,
21
22    /// Set 'username' in public key information
23    #[cfg_attr(feature = "clap", clap(long))]
24    pub username: Option<String>,
25
26    /// Add 'created' date in public key information
27    #[cfg_attr(feature = "clap", clap(long))]
28    pub with_created: bool,
29}
30
31/// Add the current key to the repository.
32///
33/// # Type Parameters
34///
35/// * `P` - The progress bar type
36/// * `S` - The state the repository is in
37///
38/// # Arguments
39///
40/// * `repo` - The repository to add the key to
41/// * `opts` - The key options to use
42/// * `pass` - The password to encrypt the key with
43///
44/// # Errors
45///
46/// * If the key could not be serialized
47///
48/// # Returns
49///
50/// The id of the key.
51pub(crate) fn add_current_key_to_repo<S: Open>(
52    repo: &Repository<S>,
53    opts: &KeyOptions,
54    pass: &str,
55) -> RusticResult<KeyId> {
56    let key = repo.dbe().key();
57    add_key_to_repo(repo, opts, pass, *key)
58}
59
60/// Initialize a new key.
61///
62/// # Type Parameters
63///
64/// * `P` - The progress bar type
65/// * `S` - The state the repository is in
66///
67/// # Arguments
68///
69/// * `repo` - The repository to add the key to
70/// * `opts` - The key options to use
71/// * `pass` - The password to encrypt the key with
72///
73/// # Returns
74///
75/// A tuple of the key and the id of the key.
76pub(crate) fn init_key<S>(
77    repo: &Repository<S>,
78    opts: &KeyOptions,
79    pass: &str,
80) -> RusticResult<(Key, KeyId)> {
81    // generate key
82    let key = Key::new();
83    Ok((key, add_key_to_repo(repo, opts, pass, key)?))
84}
85
86/// Add a key to the repository.
87///
88/// # Arguments
89///
90/// * `repo` - The repository to add the key to
91/// * `opts` - The key options to use
92/// * `pass` - The password to encrypt the key with
93/// * `key` - The key to add
94///
95/// # Errors
96///
97/// * If the key could not be serialized.
98///
99/// # Returns
100///
101/// The id of the key.
102pub(crate) fn add_key_to_repo<S>(
103    repo: &Repository<S>,
104    opts: &KeyOptions,
105    pass: &str,
106    key: Key,
107) -> RusticResult<KeyId> {
108    let ko = opts.clone();
109    let keyfile = KeyFile::generate(key, &pass, ko.hostname, ko.username, ko.with_created)?;
110
111    let data = serde_json::to_vec(&keyfile).map_err(|err| {
112        RusticError::with_source(
113            ErrorKind::InputOutput,
114            "Failed to serialize keyfile to JSON.",
115            err,
116        )
117    })?;
118
119    let id = KeyId::from(hash(&data));
120
121    repo.be
122        .write_bytes(FileType::Key, &id, false, data.into())?;
123
124    Ok(id)
125}