nuts_container/
password.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
23#[cfg(test)]
24mod tests;
25
26use std::fmt;
27use std::rc::Rc;
28use thiserror::Error;
29
30use crate::svec::SecureVec;
31
32/// Password related error codes.
33#[derive(Debug, Error)]
34pub enum PasswordError {
35    /// No password callback is assigned to the container, thus no password
36    /// is available.
37    #[error("a password is needed by the current cipher")]
38    NoPassword,
39
40    /// The password callback generated an error, which is passed to the
41    /// variant.
42    #[error("failed to receive the password: {0}")]
43    PasswordCallback(String),
44}
45
46pub type CallbackFn = dyn Fn() -> Result<Vec<u8>, String>;
47
48pub struct PasswordStore {
49    callback: Option<Rc<CallbackFn>>,
50    value: Option<SecureVec>,
51}
52
53impl PasswordStore {
54    pub fn new(callback: Option<Rc<CallbackFn>>) -> PasswordStore {
55        PasswordStore {
56            callback,
57            value: None,
58        }
59    }
60
61    #[cfg(test)]
62    pub fn with_value(value: &[u8]) -> PasswordStore {
63        PasswordStore {
64            callback: None,
65            value: Some(value.to_vec().into()),
66        }
67    }
68
69    pub fn value(&mut self) -> Result<&[u8], PasswordError> {
70        match self.value {
71            Some(ref v) => Ok(v),
72            None => {
73                let callback = self.callback.as_ref().ok_or(PasswordError::NoPassword)?;
74                let value = callback().map_err(PasswordError::PasswordCallback)?;
75
76                Ok(self.value.insert(value.into()))
77            }
78        }
79    }
80}
81
82impl fmt::Debug for PasswordStore {
83    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
84        let callback = match self.callback {
85            Some(_) => Some(()),
86            None => None,
87        };
88
89        let value = self.value.as_ref().map(|_| "***");
90
91        fmt.debug_struct("PasswordStore")
92            .field("callback", &callback)
93            .field("value", &value)
94            .finish()
95    }
96}