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}