signstar_config/yubihsm2/
admin_credentials.rs1use serde::{Deserialize, Serialize};
4use signstar_crypto::{passphrase::Passphrase, traits::UserWithPassphrase};
5use signstar_yubihsm2::Credentials;
6
7use crate::{AdminCredentials, admin_credentials::Error};
8
9#[derive(Clone, Debug, Default, Deserialize, Serialize)]
30pub struct YubiHsm2AdminCredentials {
31 iteration: u32,
32 backup_passphrase: Passphrase,
33 administrators: Vec<Credentials>,
34}
35
36impl YubiHsm2AdminCredentials {
37 pub const DEFAULT_ID: u16 = 1;
39
40 pub const DEFAULT_PASSPHRASE: &str = "password";
42
43 pub const MINIMUM_PASSPHRASE_LENGTH_USER: usize = 8;
45
46 pub const MINIMUM_PASSPHRASE_LENGTH_BACKUP: usize = 10;
48
49 pub fn new(
59 iteration: u32,
60 backup_passphrase: Passphrase,
61 administrators: Vec<Credentials>,
62 ) -> Result<Self, crate::Error> {
63 let creds = Self {
64 iteration,
65 backup_passphrase,
66 administrators,
67 };
68 creds.validate()?;
69
70 Ok(creds)
71 }
72}
73
74impl AdminCredentials for YubiHsm2AdminCredentials {
75 fn validate(&self) -> Result<(), crate::Error> {
85 if self.administrators.is_empty() {
87 return Err(Error::AdministratorMissing.into());
88 }
89
90 for creds in self.administrators.iter() {
92 if creds.passphrase().expose_borrowed().len() < Self::MINIMUM_PASSPHRASE_LENGTH_USER {
93 return Err(Error::PassphraseTooShort {
94 context: format!("user {}", creds.user()),
95 minimum_length: Self::MINIMUM_PASSPHRASE_LENGTH_USER,
96 }
97 .into());
98 }
99 }
100
101 if self.backup_passphrase.expose_borrowed().len() < Self::MINIMUM_PASSPHRASE_LENGTH_BACKUP {
103 return Err(Error::PassphraseTooShort {
104 context: "backups".to_string(),
105 minimum_length: Self::MINIMUM_PASSPHRASE_LENGTH_USER,
106 }
107 .into());
108 }
109
110 Ok(())
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use testresult::TestResult;
117
118 use super::*;
119
120 #[test]
121 fn yubihsm2_admin_credentials_new_succeeds() -> TestResult {
122 let _creds = YubiHsm2AdminCredentials::new(
123 1,
124 Passphrase::new("backup-passphrase".to_string()),
125 vec![Credentials::new(1, Passphrase::new("password".to_string()))],
126 )?;
127
128 Ok(())
129 }
130
131 #[test]
132 fn yubihsm2_admin_credentials_new_fails_on_no_admins() -> TestResult {
133 match YubiHsm2AdminCredentials::new(
134 1,
135 Passphrase::new("backup-passphrase".to_string()),
136 Vec::new(),
137 ) {
138 Ok(creds) => {
139 panic!("Expected Error::AdministratorMissing but succeeded instead:\n{creds:?}")
140 }
141
142 Err(crate::Error::AdminSecretHandling(Error::AdministratorMissing)) => {}
143 Err(error) => panic!(
144 "Expected Error::AdministratorMissing but failed differently instead:\n{error}"
145 ),
146 }
147
148 Ok(())
149 }
150
151 #[test]
152 fn yubihsm2_admin_credentials_new_fails_on_admin_passphrase_too_short() -> TestResult {
153 match YubiHsm2AdminCredentials::new(
154 1,
155 Passphrase::new("backup-passphrase".to_string()),
156 vec![Credentials::new(1, Passphrase::new("pass".to_string()))],
157 ) {
158 Ok(creds) => {
159 panic!("Expected Error::PassphraseTooShort but succeeded instead:\n{creds:?}")
160 }
161 Err(crate::Error::AdminSecretHandling(Error::PassphraseTooShort { .. })) => {}
162 Err(error) => panic!(
163 "Expected Error::PassphraseTooShort but failed differently instead:\n{error}"
164 ),
165 }
166
167 Ok(())
168 }
169
170 #[test]
171 fn yubihsm2_admin_credentials_new_fails_on_backup_passphrase_too_short() -> TestResult {
172 match YubiHsm2AdminCredentials::new(
173 1,
174 Passphrase::new("backup".to_string()),
175 vec![Credentials::new(1, Passphrase::new("password".to_string()))],
176 ) {
177 Ok(creds) => {
178 panic!("Expected Error::PassphraseTooShort but succeeded instead:\n{creds:?}")
179 }
180 Err(crate::Error::AdminSecretHandling(Error::PassphraseTooShort { .. })) => {}
181 Err(error) => panic!(
182 "Expected Error::PassphraseTooShort but failed differently instead:\n{error}"
183 ),
184 }
185
186 Ok(())
187 }
188}