nuts_container/
options.rs

1// MIT License
2//
3// Copyright (c) 2022-2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23use nuts_backend::Backend;
24use std::rc::Rc;
25
26use crate::cipher::Cipher;
27use crate::digest::Digest;
28use crate::error::ContainerResult;
29use crate::kdf::{Kdf, KdfError};
30use crate::password::CallbackFn;
31#[cfg(doc)]
32use crate::{error::Error, Container};
33
34#[derive(Debug)]
35pub(crate) enum KdfBuilder {
36    Pbkdf2(Digest, u32, u32),
37    Kdf(Kdf),
38}
39
40impl KdfBuilder {
41    pub(crate) fn build(&self) -> Result<Kdf, KdfError> {
42        match self {
43            KdfBuilder::Pbkdf2(digest, iterations, salt_len) => {
44                Kdf::generate_pbkdf2(*digest, *iterations, *salt_len)
45            }
46            KdfBuilder::Kdf(ref kdf) => Ok(kdf.clone()),
47        }
48    }
49}
50
51/// Options used to create a new container.
52///
53/// Use the [`CreateOptionsBuilder`] utility to create a `CreateOptions`
54/// instance.
55pub struct CreateOptions {
56    pub(crate) callback: Option<Rc<CallbackFn>>,
57    pub(crate) cipher: Cipher,
58    pub(crate) kdf: KdfBuilder,
59    pub(crate) overwrite: bool,
60}
61
62/// Utility used to create a [`CreateOptions`] instance.
63pub struct CreateOptionsBuilder(CreateOptions);
64
65impl CreateOptionsBuilder {
66    /// Creates a builder instance.
67    ///
68    /// The container should use the given `cipher`.
69    pub fn new(cipher: Cipher) -> Self {
70        let kdf = if cipher == Cipher::None {
71            KdfBuilder::Kdf(Kdf::None)
72        } else {
73            KdfBuilder::Pbkdf2(Digest::Sha256, 65536, 16)
74        };
75
76        CreateOptionsBuilder(CreateOptions {
77            callback: None,
78            cipher,
79            kdf,
80            overwrite: false,
81        })
82    }
83
84    /// Assigns a password callback to the container.
85    ///
86    /// A password is needed, when encryption is enabled for the container.
87    /// Based on the password a wrapping key is generated, which encrypts the
88    /// secret part of the header. If encryption is enabled but no password
89    /// callback is assigned, an [`Error::NoPassword`] error is raised. If
90    /// encryption is disabled, no password is needed and an assigned callback
91    /// is never called.
92    ///
93    /// On success the callback returns the password (represented as an
94    /// [`Vec<u8>`](`Vec`)) wrapped into an [`Ok`](`Result::Ok`). On any
95    /// failure an [`Err`](`Result::Err`) with an error message must be
96    /// returned.
97    ///
98    /// [`Error::NoPassword`]: enum.Error.html#variant.NoPassword
99    pub fn with_password_callback<Cb: Fn() -> Result<Vec<u8>, String> + 'static>(
100        mut self,
101        callback: Cb,
102    ) -> Self {
103        self.0.callback = Some(Rc::new(callback));
104        self
105    }
106
107    /// Uses the given key derivation function.
108    ///
109    /// If the cipher is set to [`Cipher::None`], then the setting is
110    /// discarded;  you don't need a KDF for the None cipher.
111    pub fn with_kdf(mut self, kdf: Kdf) -> Self {
112        if self.0.cipher != Cipher::None {
113            self.0.kdf = KdfBuilder::Kdf(kdf);
114        }
115
116        self
117    }
118
119    /// Assigns a new overwrite flag to the options.
120    ///
121    /// If set to `true` an already existing backend is overwritten. If
122    /// `overwrite` is set to `false` and the requested container exists, the
123    ///  build should fail.
124    pub fn with_overwrite(mut self, overwrite: bool) -> Self {
125        self.0.overwrite = overwrite;
126        self
127    }
128
129    /// Creates the [`CreateOptions`] instance.
130    ///
131    /// Before the [`CreateOptions`] instance is created all options passed to
132    /// the builder are validated.
133    ///
134    /// # Errors
135    ///
136    /// If validation has failed an [`Error`] is returned.
137    pub fn build<B: Backend>(self) -> ContainerResult<CreateOptions, B> {
138        Ok(self.0)
139    }
140}
141
142/// Options used to open a container.
143///
144/// Use the [`OpenOptionsBuilder`] utility to create a `OpenOptions` instance.
145pub struct OpenOptions {
146    pub(crate) callback: Option<Rc<CallbackFn>>,
147}
148
149/// Utility used to create a [`OpenOptions`] instance.
150pub struct OpenOptionsBuilder(OpenOptions);
151
152impl OpenOptionsBuilder {
153    /// Creates a builder instance.
154    pub fn new() -> Self {
155        OpenOptionsBuilder(OpenOptions { callback: None })
156    }
157
158    /// Assigns a password callback to the container.
159    ///
160    /// A password is needed, when encryption is enabled for the container.
161    /// Based on the password a wrapping key is generated, which encrypts the
162    /// secret part of the header. If encryption is enabled but no password
163    /// callback is assigned, an [`Error::NoPassword`] error is raised. If
164    /// encryption is disabled, no password is needed and an assigned callback
165    /// is never called.
166    ///
167    /// On success the callback returns the password (represented as an
168    /// [`Vec<u8>`](`Vec`)) wrapped into an [`Ok`](`Result::Ok`). On any
169    /// failure an [`Err`](`Result::Err`) with an error message must be
170    /// returned.
171    ///
172    /// [`Error::NoPassword`]: enum.Error.html#variant.NoPassword
173    pub fn with_password_callback<Cb: Fn() -> Result<Vec<u8>, String> + 'static>(
174        mut self,
175        callback: Cb,
176    ) -> Self {
177        self.0.callback = Some(Rc::new(callback));
178        self
179    }
180
181    /// Creates the [`OpenOptions`] instance.
182    ///
183    /// Before the [`OpenOptions`] instance is created all options passed to
184    /// the builder are validated.
185    ///
186    /// # Errors
187    ///
188    /// If validation has failed an [`Error`] is returned.
189    pub fn build<B: Backend>(self) -> ContainerResult<OpenOptions, B> {
190        Ok(self.0)
191    }
192}
193
194impl Default for OpenOptionsBuilder {
195    fn default() -> Self {
196        Self::new()
197    }
198}
199
200/// Options used to modify a container.
201///
202/// Use the [`ModifyOptionsBuilder`] utility to create a `ModifyOptions`
203/// instance.
204pub struct ModifyOptions {
205    pub(crate) kdf: Option<Kdf>,
206    pub(crate) password: Option<Rc<CallbackFn>>,
207}
208
209/// Utility used to create a [`ModifyOptions`] instance.
210///
211/// The following example creates a [`ModifyOptions`] instance, which changes
212/// the password of the container:
213///
214/// ```
215/// let options = nuts_container::ModifyOptionsBuilder::default()
216///     .change_password(|| Ok(b"123".to_vec()));
217/// ```
218pub struct ModifyOptionsBuilder(ModifyOptions);
219
220impl ModifyOptionsBuilder {
221    /// Changes the [key derivation function][Kdf] of the container.
222    ///
223    /// **Notes**
224    ///
225    /// * If encryption is deactivated, the _key derivation function_ is
226    ///   permanently set to [`Kdf::None`]. It cannot be changed.
227    /// * If encryption is activated, the _key derivation function_ cannot be
228    ///   set to [`Kdf::None`].
229    ///
230    /// For both exceptions the `kdf` passed to the method is ignored.
231    pub fn change_kdf(mut self, kdf: Kdf) -> Self {
232        self.0.kdf = Some(kdf);
233        self
234    }
235
236    /// Change the password of the container.
237    ///
238    /// Use the given callback which returns the new password on success.
239    /// If encryption is disabled the callback is not invoked.
240    ///
241    /// On success the callback returns the password (represented as an
242    /// [`Vec<u8>`](`Vec`)) wrapped into an [`Ok`](`Result::Ok`). On any
243    /// failure an [`Err`](`Result::Err`) with an error message must be
244    /// returned.
245    pub fn change_password<Cb: Fn() -> Result<Vec<u8>, String> + 'static>(
246        mut self,
247        callback: Cb,
248    ) -> Self {
249        self.0.password = Some(Rc::new(callback));
250        self
251    }
252
253    /// Finally, creates the [`ModifyOptions`] instance.
254    pub fn build(self) -> ModifyOptions {
255        self.0
256    }
257}
258
259impl Default for ModifyOptionsBuilder {
260    fn default() -> Self {
261        Self(ModifyOptions {
262            kdf: None,
263            password: None,
264        })
265    }
266}