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