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}