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