signstar_config/yubihsm2/
admin_credentials.rs1use serde::{Deserialize, Serialize};
4use signstar_crypto::{
5 passphrase::{Passphrase, PassphrasePolicy},
6 traits::UserWithPassphrase,
7};
8use signstar_yubihsm2::{Credentials, object::WrapKey};
9
10use crate::admin_credentials::{AdminCredentials, Error};
11
12#[derive(Clone, Debug, Default, Deserialize, Serialize)]
31pub struct YubiHsm2AdminCredentials {
32 iteration: u32,
33 backup_passphrase: Passphrase,
34 administrators: Vec<Credentials>,
35}
36
37impl YubiHsm2AdminCredentials {
38 pub const DEFAULT_ID: u16 = 1;
40
41 pub const DEFAULT_PASSPHRASE: &str = "password";
43
44 pub const BACKUP_PASSPHRASE_POLICY: PassphrasePolicy = WrapKey::PASSPHRASE_POLICY;
50
51 pub const ADMIN_PASSPHRASE_POLICY: PassphrasePolicy = PassphrasePolicy { minimum_length: 30 };
53
54 pub fn new(
64 iteration: u32,
65 backup_passphrase: Passphrase,
66 administrators: Vec<Credentials>,
67 ) -> Result<Self, crate::Error> {
68 let creds = Self {
69 iteration,
70 backup_passphrase,
71 administrators,
72 };
73 creds.validate()?;
74
75 Ok(creds)
76 }
77}
78
79impl AdminCredentials for YubiHsm2AdminCredentials {
80 fn validate(&self) -> Result<(), crate::Error> {
90 if self.administrators.is_empty() {
92 return Err(Error::AdministratorMissing.into());
93 }
94
95 for creds in self.administrators.iter() {
97 creds
98 .passphrase()
99 .check_against_policy(&Self::ADMIN_PASSPHRASE_POLICY)?;
100 }
101
102 self.backup_passphrase
104 .check_against_policy(&Self::BACKUP_PASSPHRASE_POLICY)?;
105
106 Ok(())
107 }
108
109 fn iteration(&self) -> u32 {
111 self.iteration
112 }
113
114 fn backup_passphrase(&self) -> &Passphrase {
116 &self.backup_passphrase
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use testresult::TestResult;
123
124 use super::*;
125
126 #[test]
127 fn yubihsm2_admin_credentials_new_succeeds() -> TestResult {
128 let _creds = YubiHsm2AdminCredentials::new(
129 1,
130 Passphrase::new("backup-passphrase-really-just-for-testing-i-promise-but-it-is-really-really-long-sufficiently-long-really".to_string()),
131 vec![Credentials::new(
132 "1".parse()?,
133 Passphrase::new("admin-passphrase-really-just-for-testing-i-promise".to_string()),
134 )],
135 )?;
136
137 Ok(())
138 }
139
140 #[test]
141 fn yubihsm2_admin_credentials_new_fails_on_no_admins() -> TestResult {
142 match YubiHsm2AdminCredentials::new(
143 1,
144 Passphrase::new("backup-passphrase-really-just-for-testing-i-promise-but-it-is-really-really-long-sufficiently-long-really".to_string()),
145 Vec::new(),
146 ) {
147 Ok(creds) => {
148 panic!("Expected Error::AdministratorMissing but succeeded instead:\n{creds:?}")
149 }
150
151 Err(crate::Error::AdminSecretHandling(Error::AdministratorMissing)) => {}
152 Err(error) => panic!(
153 "Expected Error::AdministratorMissing but failed differently instead:\n{error}"
154 ),
155 }
156
157 Ok(())
158 }
159
160 #[test]
161 fn yubihsm2_admin_credentials_new_fails_on_admin_passphrase_too_short() -> TestResult {
162 match YubiHsm2AdminCredentials::new(
163 1,
164 Passphrase::new("backup-passphrase-really-just-for-testing-i-promise-but-it-is-really-really-long-sufficiently-long-really".to_string()),
165 vec![Credentials::new(
166 "1".parse()?,
167 Passphrase::new("short".to_string()),
168 )],
169 ) {
170 Ok(creds) => {
171 panic!("Expected Error::PassphraseTooShort but succeeded instead:\n{creds:?}")
172 }
173 Err(crate::Error::SignstarCrypto(signstar_crypto::Error::Passphrase(
174 signstar_crypto::passphrase::Error::Length { .. },
175 ))) => {}
176 Err(error) => panic!(
177 "Expected crate::Error::SignstarCrypto(signstar_crypto::Error::Passphrase(
178 signstar_crypto::passphrase::Error::Length)) but failed differently instead:\n{error}"
179 ),
180 }
181
182 Ok(())
183 }
184
185 #[test]
186 fn yubihsm2_admin_credentials_new_fails_on_backup_passphrase_too_short() -> TestResult {
187 match YubiHsm2AdminCredentials::new(
188 1,
189 Passphrase::new("short".to_string()),
190 vec![Credentials::new(
191 "1".parse()?,
192 Passphrase::new("admin-passphrase-really-just-for-testing-i-promise".to_string()),
193 )],
194 ) {
195 Ok(creds) => {
196 panic!("Expected Error::PassphraseTooShort but succeeded instead:\n{creds:?}")
197 }
198 Err(crate::Error::SignstarCrypto(signstar_crypto::Error::Passphrase(
199 signstar_crypto::passphrase::Error::Length { .. },
200 ))) => {}
201 Err(error) => panic!(
202 "Expected crate::Error::SignstarCrypto(signstar_crypto::Error::Passphrase(
203 signstar_crypto::passphrase::Error::Length)) but failed differently instead:\n{error}"
204 ),
205 }
206
207 Ok(())
208 }
209}