signstar_config/nethsm/backend.rs
1//! Backend handling for [`NetHsm`].
2//!
3//! Based on a [`NetHsm`], [`NetHsmAdminCredentials`] and a [`SignstarConfig`] this module offers
4//! the ability to populate a [`NetHsm`] backend with the help of the [`NetHsmBackend`] struct.
5//!
6//! Using [`NetHsmBackend::sync`] all users and keys configured in a [`SignstarConfig`]
7//! are created and adapted to changes upon re-run.
8//! The state representation can be found in the [`nethsm::state`][`crate::nethsm::state`] module.
9//!
10//! # Note
11//!
12//! This module only works with data for the same iteration (i.e. the iteration of the
13//! [`NetHsmAdminCredentials`] and those of the [`NetHsm`] backend must match).
14
15use std::{collections::HashSet, str::FromStr};
16
17use log::{debug, trace, warn};
18use nethsm::{
19 CryptographicKeyContext,
20 FullCredentials,
21 KeyId,
22 KeyMechanism,
23 KeyType,
24 NamespaceId,
25 NetHsm,
26 OpenPgpKeyUsageFlags,
27 Passphrase,
28 SystemState,
29 UserId,
30 UserRole,
31 Utc,
32};
33use pgp::composed::{Deserializable, SignedPublicKey};
34
35use super::Error;
36use crate::{
37 FilterUserKeys,
38 NetHsmAdminCredentials,
39 SignstarConfig,
40 UserMapping,
41 config::state::{KeyCertificateState, KeyState, UserState},
42 state::StateType,
43};
44
45/// Creates all _R-Administrators_ on a [`NetHsm`].
46///
47/// If users exist already, only their passphrase is set.
48///
49/// # Note
50///
51/// Uses the `nethsm` with the [default
52/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
53///
54/// # Errors
55///
56/// Returns an error if
57///
58/// - the default [`Administrator`][`UserRole::Administrator`] can not be retrieved from
59/// `admin_credentials`,
60/// - the default [`Administrator`][`UserRole::Administrator`] credentials cannot be used with the
61/// `nethsm`,
62/// - available users of the `nethsm` cannot be retrieved,
63/// - or one of the admin credentials cannot be added, or updated.
64fn add_system_wide_admins(
65 nethsm: &NetHsm,
66 admin_credentials: &NetHsmAdminCredentials,
67) -> Result<(), crate::Error> {
68 debug!(
69 "Setup system-wide administrators (R-Administrators) on NetHSM backend at {}",
70 nethsm.get_url()
71 );
72
73 let default_admin = &admin_credentials.get_default_administrator()?.name;
74 nethsm.use_credentials(default_admin)?;
75 let available_users = nethsm.get_users()?;
76 trace!(
77 "Available users on NetHSM: {}",
78 available_users
79 .iter()
80 .map(|user| user.to_string())
81 .collect::<Vec<_>>()
82 .join(", ")
83 );
84
85 for user in admin_credentials.get_administrators().iter() {
86 // Only add if user doesn't exist yet, else set passphrase
87 if !available_users.contains(&user.name) {
88 nethsm.add_user(
89 format!("System-wide Admin {}", user.name),
90 UserRole::Administrator,
91 user.passphrase.clone(),
92 Some(user.name.clone()),
93 )?;
94 } else {
95 nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
96 }
97 }
98 Ok(())
99}
100
101/// Retrieves the first available user in the [`Administrator`][`UserRole::Administrator`]
102/// (*N-Administrator*) role in a namespace.
103///
104/// Derives a list of users in the [`Administrator`][`UserRole::Administrator`] role in `namespace`
105/// from `available_users`.
106/// Ensures that at least one of the users is available on the `nethsm`.
107///
108/// # Errors
109///
110/// Returns an error if
111/// - user information of an *N-Administrator* cannot be retrieved,
112/// - or no *N-Administrator* is available in the `namespace`.
113fn get_first_available_namespace_admin(
114 nethsm: &NetHsm,
115 admin_credentials: &NetHsmAdminCredentials,
116 available_users: &[UserId],
117 namespace: &NamespaceId,
118) -> Result<UserId, crate::Error> {
119 debug!("Get the first available N-Administrator in namespace \"{namespace}\"");
120
121 // Retrieve the list of users that are both in the namespace and match an entry in the list of
122 // N-Administrators in the administrative credentials.
123 let namespace_admins = available_users
124 .iter()
125 .filter(|user| {
126 user.namespace() == Some(namespace)
127 && admin_credentials
128 .get_namespace_administrators()
129 .iter()
130 .any(|creds| &creds.name == *user)
131 })
132 .cloned()
133 .collect::<Vec<UserId>>();
134
135 let mut checked_namespace_admins = Vec::new();
136 for namespace_admin in namespace_admins {
137 if Into::<UserRole>::into(nethsm.get_user(&namespace_admin)?.role)
138 == UserRole::Administrator
139 {
140 checked_namespace_admins.push(namespace_admin);
141 }
142 }
143
144 debug!(
145 "All N-Administrators in namespace \"{namespace}\": {}",
146 checked_namespace_admins
147 .iter()
148 .map(|user| user.to_string())
149 .collect::<Vec<String>>()
150 .join(", ")
151 );
152
153 if checked_namespace_admins.is_empty() {
154 return Err(Error::NamespaceHasNoAdmin {
155 namespace: namespace.clone(),
156 url: nethsm.get_url(),
157 }
158 .into());
159 }
160
161 // Select the first N-Administrator in the namespace.
162 let Some(admin) = checked_namespace_admins.first() else {
163 return Err(Error::NamespaceHasNoAdmin {
164 namespace: namespace.clone(),
165 url: nethsm.get_url(),
166 }
167 .into());
168 };
169
170 Ok(admin.clone())
171}
172
173/// Sets up all _N-Administrators_ and their respective namespaces.
174///
175/// # Note
176///
177/// This function uses the `nethsm` with the [default
178/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
179/// namespace-specific _N-Administrator_ for individual operations.
180/// If this function succeeds, the `nethsm` is guaranteed to use the [default
181/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
182/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
183///
184/// # Errors
185///
186/// Returns an error if
187///
188/// - user information cannot be retrieved from the `nethsm`,
189/// - the available namespaces cannot be retrieved from the `nethsm`,
190/// - one of the N-Administrators in the `admin_credentials` is not in a namespace,
191/// - a namespace exists already, but no known N-Administrator is available for it,
192/// - an N-Administrator and its namespace exist already, but that user's passphrase cannot be set,
193/// - an N-Administrator does not yet exist and cannot be added,
194/// - a namespace does not yet exist and cannot be added,
195/// - or switching back to the default R-Administrator credentials fails.
196fn add_namespace_admins(
197 nethsm: &NetHsm,
198 admin_credentials: &NetHsmAdminCredentials,
199) -> Result<(), crate::Error> {
200 debug!(
201 "Setup namespace administrators (N-Administrators) on NetHSM backend at {}",
202 nethsm.get_url()
203 );
204
205 // Use the default R-Administrator for authentication to the backend by default.
206 let default_admin = &admin_credentials.get_default_administrator()?.name;
207 nethsm.use_credentials(default_admin)?;
208
209 let available_users = nethsm.get_users()?;
210 trace!(
211 "The available users on the NetHSM backend at {} are: {}",
212 nethsm.get_url(),
213 available_users
214 .iter()
215 .map(|user| user.to_string())
216 .collect::<Vec<String>>()
217 .join(", ")
218 );
219 let available_namespaces = nethsm.get_namespaces()?;
220 trace!(
221 "The available namespaces on the NetHSM backend at {} are: {}",
222 nethsm.get_url(),
223 available_namespaces
224 .iter()
225 .map(|namespace| namespace.to_string())
226 .collect::<Vec<String>>()
227 .join(", ")
228 );
229
230 // Extract the namespace from each namespace administrator found in the administrative
231 // credentials.
232 for user in admin_credentials.get_namespace_administrators() {
233 let Some(namespace) = user.name.namespace() else {
234 return Err(Error::NamespaceAdminHasNoNamespace {
235 user: user.name.clone(),
236 }
237 .into());
238 };
239
240 let namespace_exists = available_namespaces.contains(namespace);
241 if namespace_exists {
242 // Select the first available N-Administrator credentials for interacting with the
243 // NetHSM backend.
244 // This might be the targeted user itself!
245 nethsm.use_credentials(&get_first_available_namespace_admin(
246 nethsm,
247 admin_credentials,
248 &available_users,
249 namespace,
250 )?)?;
251 }
252
253 // If the list of available users on the NetHSM does not include the given N-Administrator,
254 // we create the user.
255 if available_users.contains(&user.name) {
256 // Set the passphrase of the user.
257 nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
258 } else {
259 nethsm.add_user(
260 format!("Namespace Admin {}", user.name),
261 UserRole::Administrator,
262 user.passphrase.clone(),
263 Some(user.name.clone()),
264 )?;
265
266 // If the namespace does not yet exist add the namespace (authenticated as the default
267 // R-Administrator).
268 if !namespace_exists {
269 nethsm.add_namespace(namespace)?;
270 }
271 }
272 // Always use the default R-Administrator again.
273 nethsm.use_credentials(default_admin)?;
274 }
275
276 Ok(())
277}
278
279/// Sets up all system-wide, non-administrative users based on provided credentials.
280///
281/// # Note
282///
283/// It is assumed that the [default
284/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and system-wide keys are
285/// already set up, before calling this function (see `add_system_wide_admins` and
286/// `add_system_wide_keys`, respectively).
287///
288/// This function uses the `nethsm` with the [default
289/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and is guaranteed to do
290/// so when it finishes.
291///
292/// # Errors
293///
294/// Returns an error if
295///
296/// - there are no matching credentials in `user_credentials` for a user in the list of all
297/// available system-wide, non-administrative users,
298/// - a user exists already, but its passphrase cannot be set,
299/// - a user does not yet exist and it cannot be added,
300/// - a user has a tag and deleting it fails,
301/// - or adding a tag to a user fails.
302fn add_non_administrative_users(
303 nethsm: &NetHsm,
304 admin_credentials: &NetHsmAdminCredentials,
305 users: &[UserMapping],
306 user_credentials: &[FullCredentials],
307) -> Result<(), crate::Error> {
308 debug!(
309 "Setup non-administrative, system-wide users on NetHSM backend at {}",
310 nethsm.get_url()
311 );
312
313 let default_admin = &admin_credentials.get_default_administrator()?.name;
314 nethsm.use_credentials(default_admin)?;
315 let available_users = nethsm.get_users()?;
316 debug!("Available users: {available_users:?}");
317
318 for mapping in users.iter() {
319 // Add all users of the mapping.
320 for (user, role, tags) in mapping
321 .get_nethsm_user_role_and_tags()
322 .iter()
323 // Only use system-wide, non-administrative users.
324 .filter(|(user, role, _tags)| !user.is_namespaced() && role != &UserRole::Administrator)
325 {
326 let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
327 return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
328 };
329
330 if available_users.contains(user) {
331 nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
332 } else {
333 nethsm.add_user(
334 format!("{role} user {user}"),
335 *role,
336 creds.passphrase.clone(),
337 Some(user.clone()),
338 )?;
339 }
340
341 // Add tags to users.
342 for tag in tags {
343 for available_tag in nethsm.get_user_tags(user)? {
344 nethsm.delete_user_tag(user, available_tag.as_str())?;
345 }
346 nethsm.add_user_tag(user, tag.as_str())?;
347 }
348 }
349 }
350
351 Ok(())
352}
353
354/// Sets up all namespaced non-administrative users.
355///
356/// # Note
357///
358/// It is assumed that _N-Administrators_ and namespaced keys are already set up, before calling
359/// this function (see `add_namespace_admins` and `add_namespaced_keys`, respectively).
360///
361/// This function uses the `nethsm` with the [default
362/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
363/// namespace-specific _N-Administrator_ for individual operations.
364/// If this function succeeds, the `nethsm` is guaranteed to use the [default
365/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
366/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
367///
368/// # Errors
369///
370/// Returns an error if
371///
372/// - a namespaced user is not in a namespace,
373/// - the namespace of a user does not exist,
374/// - the namespace of a user exists, but no usable *N-Administrator* for it is known,
375/// - there are no matching credentials in `user_credentials` for a user in the list of all,
376/// - a user exists already, but its passphrase cannot be set,
377/// - a user does not yet exist and cannot be created,
378/// - a tag cannot be removed from a user,
379/// - or a tag cannot be added to a user.
380fn add_namespaced_non_administrative_users(
381 nethsm: &NetHsm,
382 admin_credentials: &NetHsmAdminCredentials,
383 users: &[UserMapping],
384 user_credentials: &[FullCredentials],
385) -> Result<(), crate::Error> {
386 debug!(
387 "Setup non-administrative, namespaced users on NetHSM backend at {}",
388 nethsm.get_url()
389 );
390
391 // Use the default R-Administrator for authentication to the backend by default.
392 let default_admin = &admin_credentials.get_default_administrator()?.name;
393 nethsm.use_credentials(default_admin)?;
394
395 let available_users = nethsm.get_users()?;
396 let available_namespaces = nethsm.get_namespaces()?;
397
398 for mapping in users.iter() {
399 for (user, role, tags) in mapping
400 .get_nethsm_user_role_and_tags()
401 .iter()
402 // Only use non-administrative, namespaced users on the NetHSM backend.
403 .filter(|(user, role, _tags)| user.is_namespaced() && role != &UserRole::Administrator)
404 {
405 // Extract the namespace of the user and ensure that the namespace exists already.
406 let Some(namespace) = user.namespace() else {
407 return Err(Error::NamespaceUserNoNamespace { user: user.clone() }.into());
408 };
409 if !available_namespaces.contains(namespace) {
410 return Err(Error::NamespaceMissing {
411 namespace: namespace.clone(),
412 }
413 .into());
414 }
415
416 // Select the first available N-Administrator credentials for interacting with the
417 // NetHSM backend.
418 nethsm.use_credentials(&get_first_available_namespace_admin(
419 nethsm,
420 admin_credentials,
421 &available_users,
422 namespace,
423 )?)?;
424
425 // Retrieve credentials for the specific user.
426 let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
427 return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
428 };
429
430 // If the user exists already, only set its passphrase, otherwise create it.
431 if available_users.contains(user) {
432 nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
433 } else {
434 nethsm.add_user(
435 format!("{role} user {user}"),
436 *role,
437 creds.passphrase.clone(),
438 Some(user.clone()),
439 )?;
440 }
441
442 // Delete all tags of the user.
443 let available_tags = nethsm.get_user_tags(user)?;
444 for available_tag in available_tags {
445 nethsm.delete_user_tag(user, available_tag.as_str())?;
446 }
447 // Setup tags for the user.
448 for tag in tags.iter() {
449 nethsm.add_user_tag(user, tag)?;
450 }
451 }
452 }
453 // Always use the default R-Administrator again.
454 nethsm.use_credentials(default_admin)?;
455
456 Ok(())
457}
458
459/// Comparable components of a key setup between a [`NetHsm`] backend and a Signstar config.
460struct KeySetupComparison {
461 /// The type of state, that the data originates from.
462 pub state_type: StateType,
463 /// The key type of the setup.
464 pub key_type: KeyType,
465 /// The key mechanisms of the setup.
466 pub key_mechanisms: HashSet<KeyMechanism>,
467}
468
469/// Compares the key setups of a key from a Signstar config and that of a NetHSM backend.
470///
471/// Compares the [`KeyType`] and [`KeyMechanism`]s of `key_setup_a` and `key_setup_b`, which both
472/// have to be identical.
473///
474/// Emits a warning if the [`KeyType`] or list of [`KeyMechanism`]s of `key_setup_a` and
475/// `key_setup_b` do not match.
476fn compare_key_setups(
477 key_id: &KeyId,
478 namespace: Option<&NamespaceId>,
479 key_setup_a: KeySetupComparison,
480 key_setup_b: KeySetupComparison,
481) {
482 let namespace = if let Some(namespace) = namespace {
483 format!(" in namespace \"{namespace}\"")
484 } else {
485 "".to_string()
486 };
487 debug!(
488 "Compare key setup of key \"{key_id}\"{namespace} in {} (A) and {} (B)",
489 key_setup_a.state_type, key_setup_b.state_type
490 );
491
492 // Compare key type and warn about mismatches.
493 if key_setup_b.key_type != key_setup_a.key_type {
494 warn!(
495 "Key type mismatch of key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B) backend: {}!",
496 key_setup_a.state_type,
497 key_setup_a.key_type,
498 key_setup_b.state_type,
499 key_setup_b.key_type
500 );
501 }
502
503 // Compare key mechanisms and warn about mismatches.
504 if key_setup_b.key_mechanisms != key_setup_a.key_mechanisms {
505 warn!(
506 "Key mechanisms mismatch for key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B): {}!",
507 key_setup_a.state_type,
508 key_setup_a
509 .key_mechanisms
510 .iter()
511 .map(|mechanism| mechanism.to_string())
512 .collect::<Vec<String>>()
513 .join(", "),
514 key_setup_b.state_type,
515 key_setup_b
516 .key_mechanisms
517 .iter()
518 .map(|mechanism| mechanism.to_string())
519 .collect::<Vec<String>>()
520 .join(", "),
521 );
522 }
523}
524
525/// Sets up all system-wide keys.
526///
527/// Creates any missing keys and adds the configured tags for all of them.
528/// If keys exist already, deletes all tags and adds the configured ones for them.
529///
530/// # Note
531///
532/// It is assumed that all required _R-Administrators_ have already been set up (see
533/// `add_system_wide_admins`) before calling this function.
534///
535/// This function uses the `nethsm` with the [default
536/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
537///
538/// This function does not fail on mismatching keys, as it is assumed that keys are added
539/// intentionally and should not be deleted or altered.
540/// However, warnings are emitted if an existing key has a mismatching [`KeyType`] or
541/// [`KeyMechanisms`][`KeyMechanism`] from what is configured in the Signstar configuration file.
542///
543/// # Errors
544///
545/// Returns an error if
546///
547/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
548/// - the list of available keys on the NetHSM backend cannot be retrieved,
549/// - information about a single key cannot be retrieved from the NetHSM backend,
550/// - if a tag cannot be removed from an existing key,
551/// - if a tag cannot be added to an existing key,
552/// - or if a missing key cannot be created.
553fn add_system_wide_keys(
554 nethsm: &NetHsm,
555 admin_credentials: &NetHsmAdminCredentials,
556 users: &[UserMapping],
557) -> Result<(), crate::Error> {
558 debug!(
559 "Setup system-wide cryptographic keys on NetHSM backend at {}",
560 nethsm.get_url()
561 );
562
563 // Use the default R-Administrator for authentication to the backend by default.
564 let default_admin = &admin_credentials.get_default_administrator()?.name;
565 nethsm.use_credentials(default_admin)?;
566
567 let available_keys = nethsm.get_keys(None)?;
568
569 for mapping in users {
570 for (_user, key_id, key_setup, tag) in
571 mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
572 {
573 if available_keys.contains(&key_id) {
574 // Retrieve information about the key.
575 let info = nethsm.get_key(&key_id)?;
576
577 // Compare the key setups.
578 compare_key_setups(
579 &key_id,
580 None,
581 KeySetupComparison {
582 state_type: StateType::SignstarConfigNetHsm,
583 key_type: key_setup.key_type(),
584 key_mechanisms: HashSet::from_iter(key_setup.key_mechanisms().to_vec()),
585 },
586 KeySetupComparison {
587 state_type: StateType::NetHsm,
588 key_type: info
589 .r#type
590 .try_into()
591 .map_err(nethsm::Error::SignstarCryptoKey)?,
592 key_mechanisms: info.mechanisms.iter().map(Into::into).collect(),
593 },
594 );
595
596 // Remove all existing tags.
597 if let Some(available_tags) = info.restrictions.tags {
598 for available_tag in available_tags {
599 nethsm.delete_key_tag(&key_id, available_tag.as_str())?;
600 }
601 }
602 // Add the required tag to the key.
603 nethsm.add_key_tag(&key_id, tag.as_str())?;
604 } else {
605 // Add the key, including the required tag.
606 nethsm.generate_key(
607 key_setup.key_type(),
608 key_setup.key_mechanisms().to_vec(),
609 key_setup.key_length(),
610 Some(key_id.clone()),
611 Some(vec![tag]),
612 )?;
613 }
614 }
615 }
616
617 Ok(())
618}
619
620/// Sets up all namespaced keys and tags them.
621///
622/// Creates any missing keys and adds the configured tags for all of them.
623/// If keys exist already, deletes all tags and adds the configured ones for them.
624///
625/// # Note
626///
627/// It is assumed that _N-Administrators_ have already been set up, before calling
628/// this function (see `add_namespace_admins`).
629///
630/// This function uses the `nethsm` with the [default
631/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
632/// namespace-specific _N-Administrator_ for individual operations.
633/// If this function succeeds, the `nethsm` is guaranteed to use the [default
634/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
635/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
636///
637/// This function does not fail on mismatching keys, as it is assumed that keys are added
638/// intentionally and should not be deleted/altered.
639/// However, warnings are emitted if an existing key has a mismatching key type or key mechanisms
640/// from what is configured in the Signstar configuration file.
641///
642/// Opposite to the behavior of `add_system_wide_keys`, this function does not delete any tags from
643/// keys.
644/// This is due to [a bug in the NetHSM firmware], which leads to a crash when adding a tag to a
645/// key, trying to remove and then re-adding it again.
646///
647/// # Errors
648///
649/// Returns an error if
650///
651/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
652/// - retrieving the list of available users from the NetHSM backend fails,
653/// - a namespaced user mapped to a key is not in a namespace,
654/// - no usable *N-Administrator* for a namespace is known,
655/// - the available keys in the namespace cannot be retrieved,
656/// - information about a specific key in the namespace cannot be retrieved,
657/// - a tag cannot be added to an already existing key,
658/// - a new key cannot be generated,
659/// - or using the default system-wide administrator again fails.
660///
661/// [a bug in the NetHSM firmware]: https://github.com/Nitrokey/nethsm/issues/13
662fn add_namespaced_keys(
663 nethsm: &NetHsm,
664 admin_credentials: &NetHsmAdminCredentials,
665 users: &[UserMapping],
666) -> Result<(), crate::Error> {
667 debug!(
668 "Setup namespaced cryptographic keys on NetHSM backend at {}",
669 nethsm.get_url()
670 );
671
672 // Use the default R-Administrator for authentication to the backend by default.
673 let default_admin = &admin_credentials.get_default_administrator()?.name;
674 nethsm.use_credentials(default_admin)?;
675
676 let available_users = nethsm.get_users()?;
677
678 for mapping in users {
679 for (user, key_id, key_setup, tag) in
680 mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
681 {
682 debug!("Set up key \"{key_id}\" with tag {tag} for user {user}");
683
684 // Extract the namespace from the user or return an error.
685 let Some(namespace) = user.namespace() else {
686 // Note: Returning this error is not really possible, as we are explicitly
687 // requesting tuples of namespaced user, key setup and tag.
688 return Err(Error::NamespaceUserNoNamespace { user }.into());
689 };
690
691 // Select the first available N-Administrator credentials for interacting with the
692 // NetHSM backend.
693 nethsm.use_credentials(&get_first_available_namespace_admin(
694 nethsm,
695 admin_credentials,
696 &available_users,
697 namespace,
698 )?)?;
699
700 let available_keys = nethsm.get_keys(None)?;
701
702 if available_keys.contains(&key_id) {
703 let key_info = nethsm.get_key(&key_id)?;
704
705 // Compare the key setups.
706 compare_key_setups(
707 &key_id,
708 Some(namespace),
709 KeySetupComparison {
710 state_type: StateType::SignstarConfigNetHsm,
711 key_type: key_setup.key_type(),
712 key_mechanisms: HashSet::from_iter(key_setup.key_mechanisms().to_vec()),
713 },
714 KeySetupComparison {
715 state_type: StateType::NetHsm,
716 key_type: key_info
717 .r#type
718 .try_into()
719 .map_err(nethsm::Error::SignstarCryptoKey)?,
720 key_mechanisms: key_info.mechanisms.iter().map(Into::into).collect(),
721 },
722 );
723
724 // If there are tags already, check if the tag we are looking for is already set and
725 // if so, skip to the next key.
726 if let Some(available_tags) = key_info.restrictions.tags {
727 debug!(
728 "Available tags for key \"{key_id}\" in namespace {namespace}: {}",
729 available_tags.join(", ")
730 );
731 // NOTE: If the required tag is already set, continue to the next key.
732 // Without this we otherwise trigger a bug in the NetHSM firmware which
733 // breaks the connection after re-adding the tag for the key further down.
734 // (i.e. "Bad Status: HTTP version did not start with HTTP/")
735 // See https://github.com/Nitrokey/nethsm/issues/13 for details.
736 if available_tags.len() == 1 && available_tags.contains(&tag) {
737 continue;
738 }
739 }
740
741 // Add the tag to the key.
742 nethsm.add_key_tag(&key_id, tag.as_str())?;
743 } else {
744 // Add the key, including the required tag.
745 nethsm.generate_key(
746 key_setup.key_type(),
747 key_setup.key_mechanisms().to_vec(),
748 key_setup.key_length(),
749 Some(key_id.clone()),
750 Some(vec![tag]),
751 )?;
752 }
753 }
754 }
755 // Always use the default R-Administrator again.
756 nethsm.use_credentials(default_admin)?;
757
758 Ok(())
759}
760
761/// Adds OpenPGP certificates for system-wide keys that are used for OpenPGP signing.
762///
763/// # Note
764///
765/// It is assumed that the [default
766/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all system-wide keys
767/// and all system-wide non-administrative users are already set up, before calling this function
768/// (see `add_system_wide_admins`, `add_system_wide_keys` and `add_non_administrative_users`,
769/// respectively).
770///
771/// This function uses the `nethsm` with the [default
772/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
773/// system-wide, non-administrative user for individual operations.
774/// If this function succeeds, the `nethsm` is guaranteed to use the [default
775/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
776/// If this function fails, the `nethsm` may still use a system-wide, non-administrative user.
777///
778/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
779/// inconsistencies between signatures created with a previous version of a certificate and those
780/// created with a new version of the certificate, which is hard to debug.
781///
782/// # Errors
783///
784/// Returns an error if
785///
786/// - using the default *R-Administrator* fails,
787/// - retrieving the names of all system-wide users fails,
788/// - retrieving the names of all system-wide keys fails,
789/// - a user used for OpenPGP signing does not exist,
790/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
791/// - a user used for OpenPGP signing does not have a required tag,
792/// - a key used for OpenPGP signing does not exist,
793/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
794/// - a key used for OpenPGP signing does not have a required tag,
795/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
796/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
797/// create an OpenPGP certificate for the key,
798/// - or the default *R-Administrator* cannot be used to import the generated OpenPGP certificate
799/// for the key.
800fn add_system_wide_openpgp_certificates(
801 nethsm: &NetHsm,
802 admin_credentials: &NetHsmAdminCredentials,
803 users: &[UserMapping],
804) -> Result<(), crate::Error> {
805 debug!(
806 "Setup OpenPGP certificates for system-wide cryptographic keys on NetHSM backend at {}",
807 nethsm.get_url()
808 );
809
810 // Use the default R-Administrator for authentication to the backend by default.
811 let default_admin = &admin_credentials.get_default_administrator()?.name;
812 nethsm.use_credentials(default_admin)?;
813
814 let available_users = nethsm.get_users()?;
815
816 for mapping in users {
817 // Continue to the next mapping if it is not used for signing.
818 if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
819 continue;
820 }
821
822 for (user, key_id, key_setup, tag) in
823 mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
824 {
825 // Get OpenPGP User IDs and version or continue to the next user/key setup if the
826 // mapping is not used for OpenPGP signing.
827 let CryptographicKeyContext::OpenPgp { user_ids, version } = key_setup.key_context()
828 else {
829 debug!(
830 "Skip creating an OpenPGP certificate for the key \"{key_id}\" used by user \"{user}\" as it is not used in an OpenPGP context."
831 );
832 continue;
833 };
834
835 // Ensure the targeted user exists.
836 if !available_users.contains(&user) {
837 return Err(Error::UserMissing { user_id: user }.into());
838 }
839 // Ensure the required tag is assigned to the targeted user.
840 if !nethsm.get_user_tags(&user)?.contains(&tag) {
841 return Err(Error::UserMissingTag { user_id: user, tag }.into());
842 }
843
844 let available_keys = nethsm.get_keys(None)?;
845
846 // Ensure the targeted key exists.
847 if !available_keys.contains(&key_id) {
848 return Err(Error::KeyMissing { key_id }.into());
849 }
850 // Ensure the required tag is assigned to the targeted key.
851 if !nethsm
852 .get_key(&key_id)?
853 .restrictions
854 .tags
855 .is_some_and(|tags| tags.contains(&tag))
856 {
857 return Err(Error::KeyIsMissingTag { key_id, tag }.into());
858 }
859
860 // Create the OpenPGP certificate if it does not exist yet.
861 if nethsm.get_key_certificate(&key_id)?.is_none() {
862 // Ensure the first OpenPGP User ID exists.
863 let Some(user_id) = user_ids.first() else {
864 return Err(Error::OpenPgpUserIdMissing { key_id }.into());
865 };
866
867 // Switch to the dedicated user with access to the key to create an OpenPGP
868 // certificate for the key.
869 nethsm.use_credentials(&user)?;
870 let data = nethsm.create_openpgp_cert(
871 &key_id,
872 OpenPgpKeyUsageFlags::default(),
873 user_id.clone(),
874 Utc::now(),
875 *version,
876 )?;
877
878 // Switch back to the default R-Administrator for the import of the OpenPGP
879 // certificate.
880 nethsm.use_credentials(default_admin)?;
881 nethsm.import_key_certificate(&key_id, data)?;
882 }
883 }
884 }
885 // Always use the default R-Administrator again.
886 nethsm.use_credentials(default_admin)?;
887
888 Ok(())
889}
890
891/// Adds OpenPGP certificates for namespaced keys that are used for OpenPGP signing.
892///
893/// # Note
894///
895/// It is assumed that the [default
896/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all namespaced keys,
897/// all _N-Administrators_ and all namespaced non-administrative users are already set up, before
898/// calling this function (see `add_system_wide_admins`, `add_namespaced_keys`,
899/// `add_namespace_admins` and `add_namespaced_non_administrative_users`, respectively).
900///
901/// This function uses the `nethsm` with the [default
902/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
903/// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
904/// If this function succeeds, the `nethsm` is guaranteed to use the [default
905/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
906/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
907/// non-administrative user.
908///
909/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
910/// inconsistencies between signatures created with a previous version of a certificate and those
911/// created with a new version of the certificate, which is hard to debug.
912///
913/// # Errors
914///
915/// Returns an error if
916///
917/// - using the default *R-Administrator* fails,
918/// - retrieving the names of all users fails,
919/// - a namespaced user is not in a namespace,
920/// - no usable *N-Administrator* for a namespace is known,
921/// - a user used for OpenPGP signing does not exist,
922/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
923/// - a user used for OpenPGP signing does not have a required tag,
924/// - retrieving the names of all keys in a namespace fails,
925/// - a key used for OpenPGP signing does not exist,
926/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
927/// - a key used for OpenPGP signing does not have a required tag,
928/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
929/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
930/// create an OpenPGP certificate for the key,
931/// - or the *N-Administrator* cannot be used to import the generated OpenPGP certificate for the
932/// key.
933fn add_namespaced_openpgp_certificates(
934 nethsm: &NetHsm,
935 admin_credentials: &NetHsmAdminCredentials,
936 users: &[UserMapping],
937) -> Result<(), crate::Error> {
938 debug!(
939 "Setup OpenPGP certificates for namespaced cryptographic keys on NetHSM backend at {}",
940 nethsm.get_url()
941 );
942
943 // Use the default R-Administrator for authentication to the backend by default.
944 let default_admin = &admin_credentials.get_default_administrator()?.name;
945 nethsm.use_credentials(default_admin)?;
946
947 let available_users = nethsm.get_users()?;
948
949 for mapping in users {
950 // Continue to the next mapping if it is not used for signing.
951 if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
952 continue;
953 }
954
955 for (user, key_id, key_setup, tag) in
956 mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
957 {
958 // Get OpenPGP User IDs and version or continue to the next user/key setup if the
959 // mapping is not used for OpenPGP signing.
960 let CryptographicKeyContext::OpenPgp { user_ids, version } = key_setup.key_context()
961 else {
962 continue;
963 };
964
965 // Extract the namespace from the user.
966 let Some(namespace) = user.namespace() else {
967 // Note: Returning this error is not really possible, as we are explicitly
968 // requesting tuples of namespaced user, key setup and tag.
969 return Err(Error::NamespaceUserNoNamespace { user }.into());
970 };
971
972 // Select the first available N-Administrator credentials for interacting with the
973 // NetHSM backend.
974 let admin = get_first_available_namespace_admin(
975 nethsm,
976 admin_credentials,
977 &available_users,
978 namespace,
979 )?;
980 nethsm.use_credentials(&admin)?;
981
982 // Ensure the targeted user exists.
983 if !available_users.contains(&user) {
984 return Err(Error::NamespaceUserMissing {
985 user: user.clone(),
986 namespace: namespace.clone(),
987 }
988 .into());
989 }
990 // Ensure the required tag is assigned to the targeted user.
991 let user_tags = nethsm.get_user_tags(&user)?;
992 if !user_tags.contains(&tag) {
993 return Err(Error::NamespaceUserMissingTag {
994 user: user.clone(),
995 namespace: namespace.clone(),
996 tag,
997 }
998 .into());
999 }
1000
1001 let available_keys = nethsm.get_keys(None)?;
1002
1003 // Ensure the targeted key exists.
1004 if !available_keys.contains(&key_id) {
1005 return Err(Error::NamespaceKeyMissing {
1006 key_id,
1007 namespace: namespace.clone(),
1008 }
1009 .into());
1010 }
1011 // Ensure the required tag is assigned to the targeted key.
1012 let pubkey = nethsm.get_key(&key_id)?;
1013 if !pubkey
1014 .restrictions
1015 .tags
1016 .is_some_and(|tags| tags.contains(&tag))
1017 {
1018 return Err(Error::NamespaceKeyMissesTag {
1019 key_id,
1020 namespace: namespace.clone(),
1021 tag,
1022 }
1023 .into());
1024 }
1025
1026 // Create the OpenPGP certificate if it does not exist yet.
1027 if nethsm.get_key_certificate(&key_id)?.is_none() {
1028 // Ensure the first OpenPGP User ID exists.
1029 let Some(user_id) = user_ids.first() else {
1030 return Err(Error::NamespaceOpenPgpUserIdMissing {
1031 key_id,
1032 namespace: namespace.clone(),
1033 }
1034 .into());
1035 };
1036
1037 // Switch to the dedicated user with access to the key to create an OpenPGP
1038 // certificate for the key.
1039 nethsm.use_credentials(&user)?;
1040 let data = nethsm.create_openpgp_cert(
1041 &key_id,
1042 OpenPgpKeyUsageFlags::default(),
1043 user_id.clone(),
1044 Utc::now(),
1045 *version,
1046 )?;
1047
1048 // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1049 nethsm.use_credentials(&admin)?;
1050 nethsm.import_key_certificate(&key_id, data)?;
1051 }
1052 }
1053 }
1054 // Always use the default R-Administrator again.
1055 nethsm.use_credentials(default_admin)?;
1056
1057 Ok(())
1058}
1059
1060/// A NetHSM backend that provides full control over its data.
1061///
1062/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1063/// by the tracked [`NetHsmAdminCredentials`] and [`SignstarConfig`].
1064#[derive(Debug)]
1065pub struct NetHsmBackend<'a, 'b> {
1066 nethsm: NetHsm,
1067 admin_credentials: &'a NetHsmAdminCredentials,
1068 signstar_config: &'b SignstarConfig,
1069}
1070
1071impl<'a, 'b> NetHsmBackend<'a, 'b> {
1072 /// Creates a new [`NetHsmBackend`].
1073 ///
1074 /// # Errors
1075 ///
1076 /// Returns an error if
1077 ///
1078 /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1079 /// - or retrieving the default administrator from the `admin_credentials` fails.
1080 ///
1081 /// # Examples
1082 ///
1083 /// ```
1084 /// use std::collections::HashSet;
1085 ///
1086 /// use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1087 /// use signstar_config::{
1088 /// NetHsmAdminCredentials,
1089 /// AdministrativeSecretHandling,
1090 /// BackendConnection,
1091 /// NetHsmBackend,
1092 /// NonAdministrativeSecretHandling,
1093 /// SignstarConfig,
1094 /// UserMapping,
1095 /// };
1096 ///
1097 /// # fn main() -> testresult::TestResult {
1098 /// // The NetHSM connection.
1099 /// let nethsm = NetHsm::new(
1100 /// Connection::new(
1101 /// "https://example.org/api/v1".try_into()?,
1102 /// ConnectionSecurity::Unsafe,
1103 /// ),
1104 /// None,
1105 /// None,
1106 /// None,
1107 /// )?;
1108 /// // The administrative credentials.
1109 /// let admin_credentials = NetHsmAdminCredentials::new(
1110 /// 1,
1111 /// "backup-passphrase".parse()?,
1112 /// "unlock-passphrase".parse()?,
1113 /// vec![FullCredentials::new(
1114 /// "admin".parse()?,
1115 /// "admin-passphrase".parse()?,
1116 /// )],
1117 /// vec![FullCredentials::new(
1118 /// "ns1~admin".parse()?,
1119 /// "ns1-admin-passphrase".parse()?,
1120 /// )],
1121 /// )?;
1122 /// // The Signstar config.
1123 /// let signstar_config = SignstarConfig::new(
1124 /// 1,
1125 /// AdministrativeSecretHandling::ShamirsSecretSharing,
1126 /// NonAdministrativeSecretHandling::SystemdCreds,
1127 /// HashSet::from([BackendConnection::NetHsm(Connection::new(
1128 /// "https://localhost:8443/api/v1/".parse()?,
1129 /// "Unsafe".parse()?,
1130 /// ))]),
1131 /// HashSet::from([
1132 /// UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1133 /// UserMapping::SystemOnlyShareDownload {
1134 /// system_user: "ssh-share-down".parse()?,
1135 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1136 /// },
1137 /// UserMapping::SystemOnlyShareUpload {
1138 /// system_user: "ssh-share-up".parse()?,
1139 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1140 /// }]),
1141 /// )?;
1142 ///
1143 /// let nethsm_backend = NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config)?;
1144 /// # Ok(())
1145 /// # }
1146 /// ```
1147 pub fn new(
1148 nethsm: NetHsm,
1149 admin_credentials: &'a NetHsmAdminCredentials,
1150 signstar_config: &'b SignstarConfig,
1151 ) -> Result<Self, crate::Error> {
1152 debug!(
1153 "Create a new NetHSM backend for Signstar config at {}",
1154 nethsm.get_url()
1155 );
1156
1157 // Ensure that the iterations of administrative credentials and signstar config match.
1158 if admin_credentials.get_iteration() != signstar_config.get_iteration() {
1159 return Err(Error::IterationMismatch {
1160 admin_creds: admin_credentials.get_iteration(),
1161 signstar_config: signstar_config.get_iteration(),
1162 }
1163 .into());
1164 }
1165
1166 // Add all system-wide Administrators for the connection
1167 for user in admin_credentials.get_administrators() {
1168 nethsm.add_credentials(user.into());
1169 }
1170 // Add all namespace Administrators for the connection
1171 for user in admin_credentials.get_namespace_administrators() {
1172 nethsm.add_credentials(user.into());
1173 }
1174 // Use the default administrator
1175 nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1176
1177 Ok(Self {
1178 nethsm,
1179 admin_credentials,
1180 signstar_config,
1181 })
1182 }
1183
1184 /// Returns a reference to the tracked [`NetHsm`].
1185 pub fn nethsm(&self) -> &NetHsm {
1186 &self.nethsm
1187 }
1188
1189 /// Unlocks a locked [`NetHsm`] backend.
1190 pub(crate) fn unlock_nethsm(&self) -> Result<(), crate::Error> {
1191 Ok(self.nethsm.unlock(Passphrase::new(
1192 self.admin_credentials.get_unlock_passphrase().into(),
1193 ))?)
1194 }
1195
1196 /// Retrieves the state for all users on the [`NetHsm`] backend.
1197 ///
1198 /// # Note
1199 ///
1200 /// Uses the `nethsm` with the [default
1201 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
1202 ///
1203 /// # Errors
1204 ///
1205 /// Returns an error if
1206 ///
1207 /// - using the credentials of the default *R-Administrator* fails,
1208 /// - retrieving all user names of the NetHSM backend fails,
1209 /// - retrieving information about a specific NetHSM user fails,
1210 /// - or retrieving the tags of an *Operator* user fails.
1211 pub(crate) fn user_states(&self) -> Result<Vec<UserState>, crate::Error> {
1212 // Use the default R-Administrator.
1213 self.nethsm
1214 .use_credentials(&self.admin_credentials.get_default_administrator()?.name)?;
1215
1216 let users = {
1217 let mut users: Vec<UserState> = Vec::new();
1218
1219 for user_id in self.nethsm.get_users()? {
1220 let user_data = self.nethsm.get_user(&user_id)?;
1221 let tags = if user_data.role == UserRole::Operator.into() {
1222 self.nethsm.get_user_tags(&user_id)?
1223 } else {
1224 Vec::new()
1225 };
1226
1227 users.push(UserState {
1228 name: user_id,
1229 role: user_data.role.into(),
1230 tags,
1231 });
1232 }
1233
1234 users
1235 };
1236
1237 Ok(users)
1238 }
1239
1240 /// Retrieves the state of a key certificate on the [`NetHsm`] backend.
1241 ///
1242 /// Key certificates may be retrieved for system-wide keys or namespaced keys.
1243 /// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1244 /// retrieved.
1245 ///
1246 /// # Note
1247 ///
1248 /// It is assumed that the current credentials for the `nethsm` provide access to the key
1249 /// certificate of key `key_id`.
1250 fn key_certificate_state(
1251 &self,
1252 key_id: &KeyId,
1253 namespace: Option<&NamespaceId>,
1254 ) -> KeyCertificateState {
1255 // Provide a dedicated string for log messages in case a namespace is used.
1256 let namespace = if let Some(namespace) = namespace {
1257 format!(" in namespace \"{namespace}\"")
1258 } else {
1259 "".to_string()
1260 };
1261
1262 match self.nethsm.get_key_certificate(key_id) {
1263 Ok(Some(key_cert)) => {
1264 let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1265 Ok((public_key, _armor_header)) => public_key,
1266 Err(error) => {
1267 let message = format!(
1268 "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1269 );
1270 debug!("{message}");
1271 return KeyCertificateState::NotAnOpenPgpCertificate { message };
1272 }
1273 };
1274
1275 match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1276 Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1277 Err(error) => {
1278 let message = format!(
1279 "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1280 );
1281 debug!("{message}");
1282 KeyCertificateState::NotACryptographicKeyContext { message }
1283 }
1284 }
1285 }
1286 Ok(None) => KeyCertificateState::Empty,
1287 Err(error) => {
1288 let message = error.to_string();
1289 debug!("{message}");
1290 KeyCertificateState::Error { message }
1291 }
1292 }
1293 }
1294
1295 /// Retrieves the state for all keys on the [`NetHsm`] backend.
1296 ///
1297 /// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1298 /// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1299 ///
1300 /// # Note
1301 ///
1302 /// This function uses the `nethsm` with the [default
1303 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1304 /// namespace-specific _N-Administrator_ for individual operations.
1305 /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1306 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1307 /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1308 ///
1309 ///
1310 /// # Errors
1311 ///
1312 /// Returns an error if
1313 ///
1314 /// - using the default *R-Administrator* for authentication against the backend fails,
1315 /// - retrieving the names of all system-wide keys on the backend fails,
1316 /// - retrieving information on a specific system-wide key on the backend fails,
1317 /// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1318 /// - using the credentials of an *N-Administrator* fails,
1319 /// - retrieving the names of all namespaced keys on the backend fails,
1320 /// - or retrieving information on a specific namespaced key on the backend fails.
1321 pub(crate) fn key_states(&self) -> Result<Vec<KeyState>, crate::Error> {
1322 // Use the default administrator
1323 let default_admin = &self.admin_credentials.get_default_administrator()?.name;
1324 self.nethsm.use_credentials(default_admin)?;
1325
1326 let mut keys = Vec::new();
1327 // Get the state of system-wide keys.
1328 for key_id in self.nethsm.get_keys(None)? {
1329 let key = self.nethsm.get_key(&key_id)?;
1330 let key_context = self.key_certificate_state(&key_id, None);
1331
1332 keys.push(KeyState {
1333 name: key_id,
1334 namespace: None,
1335 tags: key.restrictions.tags.unwrap_or_default(),
1336 key_type: key
1337 .r#type
1338 .try_into()
1339 .map_err(nethsm::Error::SignstarCryptoKey)?,
1340 mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1341 key_cert_state: key_context,
1342 });
1343 }
1344
1345 let mut seen_namespaces = HashSet::new();
1346 // Get the state of namespaced keys.
1347 for user_id in self
1348 .admin_credentials
1349 .get_namespace_administrators()
1350 .iter()
1351 .map(|creds| creds.name.clone())
1352 {
1353 // Extract the namespace of the user and ensure that the namespace exists already.
1354 let Some(namespace) = user_id.namespace() else {
1355 return Err(Error::NamespaceUserNoNamespace {
1356 user: user_id.clone(),
1357 }
1358 .into());
1359 };
1360
1361 // Only extract key information for the namespace if we have not already looked at it.
1362 if seen_namespaces.contains(namespace) {
1363 continue;
1364 }
1365 seen_namespaces.insert(namespace.clone());
1366
1367 self.nethsm.use_credentials(&user_id)?;
1368 for key_id in self.nethsm.get_keys(None)? {
1369 let key = self.nethsm.get_key(&key_id)?;
1370 let key_context = self.key_certificate_state(&key_id, Some(namespace));
1371
1372 keys.push(KeyState {
1373 name: key_id,
1374 namespace: Some(namespace.clone()),
1375 tags: key.restrictions.tags.unwrap_or_default(),
1376 key_type: key
1377 .r#type
1378 .try_into()
1379 .map_err(nethsm::Error::SignstarCryptoKey)?,
1380 mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1381 key_cert_state: key_context,
1382 });
1383 }
1384 }
1385
1386 // Always use the default *R-Administrator* again.
1387 self.nethsm.use_credentials(default_admin)?;
1388
1389 Ok(keys)
1390 }
1391
1392 /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1393 /// non-administrative roles.
1394 ///
1395 /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1396 /// Then works down the following list to
1397 ///
1398 /// - create _R-Administrators_,
1399 /// - or set their passphrase if they exist already,
1400 /// - create system-wide keys and add tags to them,
1401 /// - or remove all tags from existing keys and only add the configured tags,
1402 /// - create users in the system-wide, non-administrative roles (i.e.
1403 /// [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1404 /// [`Operator`][`UserRole::Operator`]),
1405 /// - or set their passphrase if they exist already,
1406 /// - create OpenPGP certificates for system-wide keys,
1407 /// - or do nothing if they exist already,
1408 /// - create _N-Administrators_ and their respective namespaces,
1409 /// - or set their passphrase if they exist already,
1410 /// - create namespaced keys and add tags to them,
1411 /// - or remove all tags from existing keys and only add the configured tags,
1412 /// - create users in the namespaced, non-administrative roles (i.e.
1413 /// [`Operator`][`UserRole::Operator`]),
1414 /// - or set their passphrase if they exist already,
1415 /// - and create OpenPGP certificates for namespaced keys,
1416 /// - or do nothing if they exist already.
1417 ///
1418 /// # Note
1419 ///
1420 /// This function uses the `nethsm` with the [default
1421 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1422 /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1423 /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1424 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1425 /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1426 /// non-administrative user.
1427 ///
1428 /// # Errors
1429 ///
1430 /// Returns an error if
1431 ///
1432 /// - retrieving the state of the [`NetHsm`] backend fails,
1433 /// - provisioning an unprovisioned [`NetHsm`] fails,
1434 /// - unlocking a locked [`NetHsm`] backend fails,
1435 /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1436 /// - adding system-wide keys fails,
1437 /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1438 /// [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1439 /// - adding OpenPGP certificates for system-wide keys fails,
1440 /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1441 /// their respective namespace fails,
1442 /// - adding namespaced keys fails,
1443 /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1444 /// - or adding OpenPGP certificates for namespaced keys fails.
1445 pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1446 debug!(
1447 "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1448 self.nethsm.get_url()
1449 );
1450
1451 // Extract the user mappings.
1452 let users = self
1453 .signstar_config
1454 .iter_user_mappings()
1455 .cloned()
1456 .collect::<Vec<UserMapping>>();
1457
1458 match self.nethsm.state()? {
1459 SystemState::Unprovisioned => {
1460 debug!(
1461 "Unprovisioned NetHSM backend detected at {}",
1462 self.nethsm.get_url()
1463 );
1464
1465 self.nethsm.provision(
1466 Passphrase::from_str(self.admin_credentials.get_unlock_passphrase()).map_err(
1467 |source| {
1468 crate::Error::NetHsm(nethsm::Error::SignstarCryptoPassphrase(source))
1469 },
1470 )?,
1471 self.admin_credentials
1472 .get_default_administrator()?
1473 .passphrase
1474 .clone(),
1475 nethsm::Utc::now(),
1476 )?;
1477 }
1478 SystemState::Locked => {
1479 debug!(
1480 "Locked NetHSM backend detected at {}",
1481 self.nethsm.get_url()
1482 );
1483
1484 self.nethsm.unlock(Passphrase::new(
1485 self.admin_credentials.get_unlock_passphrase().into(),
1486 ))?;
1487 }
1488 SystemState::Operational => {
1489 debug!(
1490 "Operational NetHSM backend detected at {}",
1491 self.nethsm.get_url()
1492 );
1493 }
1494 }
1495
1496 // Add any missing users and keys.
1497 add_system_wide_admins(&self.nethsm, self.admin_credentials)?;
1498 add_system_wide_keys(&self.nethsm, self.admin_credentials, &users)?;
1499 add_non_administrative_users(
1500 &self.nethsm,
1501 self.admin_credentials,
1502 &users,
1503 user_credentials,
1504 )?;
1505 add_system_wide_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1506 add_namespace_admins(&self.nethsm, self.admin_credentials)?;
1507 add_namespaced_keys(&self.nethsm, self.admin_credentials, &users)?;
1508 add_namespaced_non_administrative_users(
1509 &self.nethsm,
1510 self.admin_credentials,
1511 &users,
1512 user_credentials,
1513 )?;
1514 add_namespaced_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1515
1516 Ok(())
1517 }
1518}
1519
1520#[cfg(test)]
1521mod tests {
1522 use std::collections::HashSet;
1523
1524 use log::LevelFilter;
1525 use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1526 use signstar_common::logging::setup_logging;
1527 use testresult::TestResult;
1528
1529 use super::*;
1530 use crate::{
1531 AdministrativeSecretHandling,
1532 NonAdministrativeSecretHandling,
1533 config::base::BackendConnection,
1534 };
1535
1536 /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1537 /// [`NetHsmAdminCredentials`] and [`SignstarConfig`].
1538 #[test]
1539 fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1540 setup_logging(LevelFilter::Debug)?;
1541
1542 let nethsm = NetHsm::new(
1543 Connection::new(
1544 "https://example.org/api/v1".try_into()?,
1545 ConnectionSecurity::Unsafe,
1546 ),
1547 None,
1548 None,
1549 None,
1550 )?;
1551 // The administrative credentials.
1552 let admin_credentials = NetHsmAdminCredentials::new(
1553 // this is different from the one in the Signstar config.
1554 2,
1555 "backup-passphrase".parse()?,
1556 "unlock-passphrase".parse()?,
1557 vec![FullCredentials::new(
1558 "admin".parse()?,
1559 "admin-passphrase".parse()?,
1560 )],
1561 vec![FullCredentials::new(
1562 "ns1~admin".parse()?,
1563 "ns1-admin-passphrase".parse()?,
1564 )],
1565 )?;
1566 // The Signstar config.
1567 let signstar_config = SignstarConfig::new(
1568 1,
1569 AdministrativeSecretHandling::ShamirsSecretSharing,
1570 NonAdministrativeSecretHandling::SystemdCreds,
1571 HashSet::from([BackendConnection::NetHsm(Connection::new(
1572 "https://localhost:8443/api/v1/".parse()?,
1573 "Unsafe".parse()?,
1574 ))]),
1575 HashSet::from([
1576 UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1577 UserMapping::SystemOnlyShareDownload {
1578 system_user: "ssh-share-down".parse()?,
1579 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1580 },
1581 UserMapping::SystemOnlyShareUpload {
1582 system_user: "ssh-share-up".parse()?,
1583 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1584 }]),
1585 )?;
1586 let nethsm_backend_result =
1587 NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
1588
1589 assert!(
1590 nethsm_backend_result.is_err(),
1591 "Test should have failed, but succeeded"
1592 );
1593 assert!(
1594 matches!(
1595 nethsm_backend_result,
1596 Err(crate::Error::NetHsmBackend(Error::IterationMismatch {
1597 admin_creds: _,
1598 signstar_config: _
1599 }))
1600 ),
1601 "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
1602 );
1603
1604 Ok(())
1605 }
1606}