signstar_config/nethsm/backend.rs
1//! Backend handling for [`NetHsm`].
2//!
3//! Based on a [`NetHsm`], [`NetHsmAdminCredentials`] and a [`Config`] this module offers
4//! the ability to populate a [`NetHsm`] backend with the help of the [`NetHsmBackend`] struct.
5//!
6//! Using [`NetHsmBackend::sync`] all users and keys configured in a [`Config`]
7//! are created and adapted to changes upon re-run.
8//! The state representation can be found in the [`nethsm::state`][`crate::nethsm::state`] module.
9//!
10//! # Note
11//!
12//! This module only works with data for the same iteration (i.e. the iteration of the
13//! [`NetHsmAdminCredentials`] and those of the [`NetHsm`] backend must match).
14
15use std::{collections::HashSet, fmt::Display, str::FromStr};
16
17use log::{debug, info, trace, warn};
18use nethsm::{
19 CryptographicKeyContext,
20 FullCredentials,
21 KeyId,
22 KeyMechanism,
23 KeyType,
24 NamespaceId,
25 NetHsm,
26 OpenPgpKeyUsageFlags,
27 Passphrase,
28 SystemState,
29 Timestamp,
30 UserId,
31 UserRole,
32};
33use pgp::composed::{Deserializable, SignedPublicKey};
34
35use crate::{
36 config::{Config, KeyCertificateState},
37 nethsm::{
38 NetHsmAdminCredentials,
39 NetHsmConfig,
40 NetHsmUserKeysFilter,
41 NetHsmUserMapping,
42 error::Error,
43 },
44 state::{StateOrigin, StateOriginInfo},
45};
46
47/// Creates all _R-Administrators_ on a [`NetHsm`].
48///
49/// If users exist already, only their passphrase is set.
50///
51/// # Note
52///
53/// Uses the `nethsm` with the [default
54/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
55///
56/// # Errors
57///
58/// Returns an error if
59///
60/// - the default [`Administrator`][`UserRole::Administrator`] can not be retrieved from
61/// `admin_credentials`,
62/// - the default [`Administrator`][`UserRole::Administrator`] credentials cannot be used with the
63/// `nethsm`,
64/// - available users of the `nethsm` cannot be retrieved,
65/// - or one of the admin credentials cannot be added, or updated.
66fn add_system_wide_admins(
67 nethsm: &NetHsm,
68 admin_credentials: &NetHsmAdminCredentials,
69 nethsm_config: &NetHsmConfig,
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 user_list = admin_credentials.administrators_in_config(nethsm_config);
77
78 let default_admin = &admin_credentials.get_default_administrator()?.name;
79 nethsm.use_credentials(default_admin)?;
80 let available_users = nethsm.get_users()?;
81 trace!(
82 "Available users on NetHSM: {}",
83 available_users
84 .iter()
85 .map(|user| user.to_string())
86 .collect::<Vec<_>>()
87 .join(", ")
88 );
89
90 for user in user_list {
91 // Only add if user doesn't exist yet, else set passphrase
92 if !available_users.contains(&user.name) {
93 nethsm.add_user(
94 format!("System-wide Admin {}", user.name),
95 UserRole::Administrator,
96 user.passphrase.clone(),
97 Some(user.name.clone()),
98 )?;
99 } else {
100 nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
101 }
102 }
103 Ok(())
104}
105
106/// Retrieves the first available user in the [`Administrator`][`UserRole::Administrator`]
107/// (*N-Administrator*) role in a namespace.
108///
109/// Derives a list of users in the [`Administrator`][`UserRole::Administrator`] role in `namespace`
110/// from `available_users`.
111/// Ensures that at least one of the users is available on the `nethsm`.
112///
113/// # Errors
114///
115/// Returns an error if
116/// - user information of an *N-Administrator* cannot be retrieved,
117/// - or no *N-Administrator* is available in the `namespace`.
118fn get_first_available_namespace_admin(
119 nethsm: &NetHsm,
120 admin_credentials: &NetHsmAdminCredentials,
121 available_users: &[UserId],
122 namespace: &NamespaceId,
123) -> Result<UserId, crate::Error> {
124 debug!("Get the first available N-Administrator in namespace \"{namespace}\"");
125
126 // Retrieve the list of users that are both in the namespace and match an entry in the list of
127 // N-Administrators in the administrative credentials.
128 let namespace_admins = available_users
129 .iter()
130 .filter(|user| {
131 user.namespace() == Some(namespace)
132 && admin_credentials
133 .get_namespace_administrators()
134 .iter()
135 .any(|creds| &creds.name == *user)
136 })
137 .cloned()
138 .collect::<Vec<UserId>>();
139
140 let mut checked_namespace_admins = Vec::new();
141 for namespace_admin in namespace_admins {
142 if Into::<UserRole>::into(nethsm.get_user(&namespace_admin)?.role)
143 == UserRole::Administrator
144 {
145 checked_namespace_admins.push(namespace_admin);
146 }
147 }
148
149 debug!(
150 "All N-Administrators in namespace \"{namespace}\": {}",
151 checked_namespace_admins
152 .iter()
153 .map(|user| user.to_string())
154 .collect::<Vec<String>>()
155 .join(", ")
156 );
157
158 if checked_namespace_admins.is_empty() {
159 return Err(Error::NamespaceHasNoAdmin {
160 namespace: namespace.clone(),
161 url: nethsm.get_url(),
162 }
163 .into());
164 }
165
166 // Select the first N-Administrator in the namespace.
167 let Some(admin) = checked_namespace_admins.first() else {
168 return Err(Error::NamespaceHasNoAdmin {
169 namespace: namespace.clone(),
170 url: nethsm.get_url(),
171 }
172 .into());
173 };
174
175 Ok(admin.clone())
176}
177
178/// Sets up all _N-Administrators_ and their respective namespaces.
179///
180/// # Note
181///
182/// This function uses the `nethsm` with the [default
183/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
184/// namespace-specific _N-Administrator_ for individual operations.
185/// If this function succeeds, the `nethsm` is guaranteed to use the [default
186/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
187/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
188///
189/// # Errors
190///
191/// Returns an error if
192///
193/// - user information cannot be retrieved from the `nethsm`,
194/// - the available namespaces cannot be retrieved from the `nethsm`,
195/// - one of the N-Administrators in the `admin_credentials` is not in a namespace,
196/// - a namespace exists already, but no known N-Administrator is available for it,
197/// - an N-Administrator and its namespace exist already, but that user's passphrase cannot be set,
198/// - an N-Administrator does not yet exist and cannot be added,
199/// - a namespace does not yet exist and cannot be added,
200/// - or switching back to the default R-Administrator credentials fails.
201fn add_namespace_admins(
202 nethsm: &NetHsm,
203 admin_credentials: &NetHsmAdminCredentials,
204 nethsm_config: &NetHsmConfig,
205) -> Result<(), crate::Error> {
206 debug!(
207 "Setup namespace administrators (N-Administrators) on NetHSM backend at {}",
208 nethsm.get_url()
209 );
210
211 let user_list = admin_credentials.namespace_administrators_in_config(nethsm_config);
212
213 // Use the default R-Administrator for authentication to the backend by default.
214 let default_admin = &admin_credentials.get_default_administrator()?.name;
215 nethsm.use_credentials(default_admin)?;
216
217 let available_users = nethsm.get_users()?;
218 trace!(
219 "The available users on the NetHSM backend at {} are: {}",
220 nethsm.get_url(),
221 available_users
222 .iter()
223 .map(|user| user.to_string())
224 .collect::<Vec<String>>()
225 .join(", ")
226 );
227 let available_namespaces = nethsm.get_namespaces()?;
228 trace!(
229 "The available namespaces on the NetHSM backend at {} are: {}",
230 nethsm.get_url(),
231 available_namespaces
232 .iter()
233 .map(|namespace| namespace.to_string())
234 .collect::<Vec<String>>()
235 .join(", ")
236 );
237
238 // Extract the namespace from each namespace administrator found in the administrative
239 // credentials.
240 for user in user_list {
241 let Some(namespace) = user.name.namespace() else {
242 return Err(Error::NamespaceAdminHasNoNamespace {
243 user: user.name.clone(),
244 }
245 .into());
246 };
247
248 let namespace_exists = available_namespaces.contains(namespace);
249 if namespace_exists {
250 // Select the first available N-Administrator credentials for interacting with the
251 // NetHSM backend.
252 // This might be the targeted user itself!
253 nethsm.use_credentials(&get_first_available_namespace_admin(
254 nethsm,
255 admin_credentials,
256 &available_users,
257 namespace,
258 )?)?;
259 }
260
261 // If the list of available users on the NetHSM does not include the given N-Administrator,
262 // we create the user.
263 if available_users.contains(&user.name) {
264 // Set the passphrase of the user.
265 nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
266 } else {
267 nethsm.add_user(
268 format!("Namespace Admin {}", user.name),
269 UserRole::Administrator,
270 user.passphrase.clone(),
271 Some(user.name.clone()),
272 )?;
273
274 // If the namespace does not yet exist add the namespace (authenticated as the default
275 // R-Administrator).
276 if !namespace_exists {
277 nethsm.add_namespace(namespace)?;
278 }
279 }
280 // Always use the default R-Administrator again.
281 nethsm.use_credentials(default_admin)?;
282 }
283
284 Ok(())
285}
286
287/// Sets up all system-wide, non-administrative users based on provided credentials.
288///
289/// # Note
290///
291/// It is assumed that the [default
292/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and system-wide keys are
293/// already set up, before calling this function (see `add_system_wide_admins` and
294/// `add_system_wide_keys`, respectively).
295///
296/// This function uses the `nethsm` with the [default
297/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and is guaranteed to do
298/// so when it finishes.
299///
300/// # Errors
301///
302/// Returns an error if
303///
304/// - there are no matching credentials in `user_credentials` for a user in the list of all
305/// available system-wide, non-administrative users,
306/// - a user exists already, but its passphrase cannot be set,
307/// - a user does not yet exist and it cannot be added,
308/// - a user has a tag and deleting it fails,
309/// - or adding a tag to a user fails.
310fn add_non_administrative_users(
311 nethsm: &NetHsm,
312 admin_credentials: &NetHsmAdminCredentials,
313 user_mappings: &[&NetHsmUserMapping],
314 user_credentials: &[FullCredentials],
315) -> Result<(), crate::Error> {
316 debug!(
317 "Setup non-administrative, system-wide users on NetHSM backend at {}",
318 nethsm.get_url()
319 );
320
321 let default_admin = &admin_credentials.get_default_administrator()?.name;
322 nethsm.use_credentials(default_admin)?;
323 let available_users = nethsm.get_users()?;
324 debug!("Available users: {available_users:?}");
325
326 let user_data_list = user_mappings
327 .iter()
328 .filter_map(|user_mapping| {
329 let mut user_data_set = user_mapping.nethsm_config_user_data();
330 // We are only interested in mappings that define at least one system-wide,
331 // non-administrative NetHSM backend user.
332 user_data_set
333 .retain(|data| !data.user.is_namespaced() && data.role != UserRole::Administrator);
334 if user_data_set.is_empty() {
335 return None;
336 }
337
338 Some(user_data_set)
339 })
340 .flatten()
341 .collect::<Vec<_>>();
342
343 if user_data_list.is_empty() {
344 debug!(
345 "No non-administrative, system-wide users to setup on NetHSM backend at {}",
346 nethsm.get_url()
347 );
348 return Ok(());
349 }
350
351 let default_admin = &admin_credentials.get_default_administrator()?.name;
352 nethsm.use_credentials(default_admin)?;
353 let available_users = nethsm.get_users()?;
354 debug!("Available users: {available_users:?}");
355
356 for user_data in user_data_list {
357 let Some(creds) = user_credentials
358 .iter()
359 .find(|creds| &creds.name == user_data.user)
360 else {
361 return Err(Error::UserMissingPassphrase {
362 user: user_data.user.clone(),
363 }
364 .into());
365 };
366
367 if available_users.contains(user_data.user) {
368 nethsm.set_user_passphrase(user_data.user.clone(), creds.passphrase.clone())?;
369 } else {
370 nethsm.add_user(
371 format!("{} user {}", user_data.role, user_data.user),
372 user_data.role,
373 creds.passphrase.clone(),
374 Some(user_data.user.clone()),
375 )?;
376 }
377
378 if user_data.role == UserRole::Operator {
379 // First, delete all existing tags from user.
380 for available_tag in nethsm.get_user_tags(user_data.user)? {
381 nethsm.delete_user_tag(user_data.user, available_tag.as_str())?;
382 }
383 // Then, add optional tag to user.
384 if let Some(tag) = user_data.tag {
385 nethsm.add_user_tag(user_data.user, tag)?;
386 }
387 }
388 }
389
390 Ok(())
391}
392
393/// Sets up all namespaced non-administrative users.
394///
395/// # Note
396///
397/// It is assumed that _N-Administrators_ and namespaced keys are already set up, before calling
398/// this function (see `add_namespace_admins` and `add_namespaced_keys`, respectively).
399///
400/// This function uses the `nethsm` with the [default
401/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
402/// namespace-specific _N-Administrator_ for individual operations.
403/// If this function succeeds, the `nethsm` is guaranteed to use the [default
404/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
405/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
406///
407/// # Errors
408///
409/// Returns an error if
410///
411/// - a namespaced user is not in a namespace,
412/// - the namespace of a user does not exist,
413/// - the namespace of a user exists, but no usable *N-Administrator* for it is known,
414/// - there are no matching credentials in `user_credentials` for a user in the list of all,
415/// - a user exists already, but its passphrase cannot be set,
416/// - a user does not yet exist and cannot be created,
417/// - a tag cannot be removed from a user,
418/// - or a tag cannot be added to a user.
419fn add_namespaced_non_administrative_users(
420 nethsm: &NetHsm,
421 admin_credentials: &NetHsmAdminCredentials,
422 user_mappings: &[&NetHsmUserMapping],
423 user_credentials: &[FullCredentials],
424) -> Result<(), crate::Error> {
425 debug!(
426 "Setup non-administrative, namespaced users on NetHSM backend at {}",
427 nethsm.get_url()
428 );
429
430 // Use the default R-Administrator for authentication to the backend by default.
431 let default_admin = &admin_credentials.get_default_administrator()?.name;
432 nethsm.use_credentials(default_admin)?;
433
434 let available_users = nethsm.get_users()?;
435 let available_namespaces = nethsm.get_namespaces()?;
436 let user_data_list = user_mappings
437 .iter()
438 .filter_map(|user_mapping| {
439 let mut user_data_set = user_mapping.nethsm_config_user_data();
440 // We are only interested in mappings that define at least one namespaced,
441 // non-administrative NetHSM backend user.
442 user_data_set
443 .retain(|data| data.user.is_namespaced() && data.role != UserRole::Administrator);
444 if user_data_set.is_empty() {
445 return None;
446 }
447
448 Some(user_data_set)
449 })
450 .flatten()
451 .collect::<Vec<_>>();
452
453 for user_data in user_data_list {
454 // Extract the namespace of the user and ensure that the namespace exists already.
455 let Some(namespace) = user_data.user.namespace() else {
456 return Err(Error::NamespaceUserNoNamespace {
457 user: user_data.user.clone(),
458 }
459 .into());
460 };
461 if !available_namespaces.contains(namespace) {
462 return Err(Error::NamespaceMissing {
463 namespace: namespace.clone(),
464 }
465 .into());
466 }
467
468 // Select the first available N-Administrator credentials for interacting with the
469 // NetHSM backend.
470 nethsm.use_credentials(&get_first_available_namespace_admin(
471 nethsm,
472 admin_credentials,
473 &available_users,
474 namespace,
475 )?)?;
476
477 // Retrieve credentials for the specific user.
478 let Some(creds) = user_credentials
479 .iter()
480 .find(|creds| &creds.name == user_data.user)
481 else {
482 return Err(Error::UserMissingPassphrase {
483 user: user_data.user.clone(),
484 }
485 .into());
486 };
487
488 // If the user exists already, only set its passphrase, otherwise create it.
489 if available_users.contains(user_data.user) {
490 nethsm.set_user_passphrase(user_data.user.clone(), creds.passphrase.clone())?;
491 } else {
492 nethsm.add_user(
493 format!("{} user {}", user_data.role, user_data.user),
494 user_data.role,
495 creds.passphrase.clone(),
496 Some(user_data.user.clone()),
497 )?;
498 }
499
500 if user_data.role == UserRole::Operator {
501 // First, delete all existing tags from user.
502 for available_tag in nethsm.get_user_tags(user_data.user)? {
503 nethsm.delete_user_tag(user_data.user, available_tag.as_str())?;
504 }
505 // Then, add optional tag to user.
506 if let Some(tag) = user_data.tag {
507 nethsm.add_user_tag(user_data.user, tag)?;
508 }
509 }
510 }
511
512 // Always use the default R-Administrator again.
513 nethsm.use_credentials(default_admin)?;
514
515 Ok(())
516}
517
518/// Comparable components of a key setup between a [`NetHsm`] backend and a Signstar config.
519struct KeySetupComparison {
520 /// The origin of the state.
521 pub state_origin: StateOrigin,
522 /// The key type of the setup.
523 pub key_type: KeyType,
524 /// The key mechanisms of the setup.
525 pub key_mechanisms: HashSet<KeyMechanism>,
526}
527
528/// Compares the key setups of a key from a Signstar config and that of a NetHSM backend.
529///
530/// Compares the [`KeyType`] and [`KeyMechanism`]s of `key_setup_a` and `key_setup_b`, which both
531/// have to be identical.
532///
533/// Emits a warning if the [`KeyType`] or list of [`KeyMechanism`]s of `key_setup_a` and
534/// `key_setup_b` do not match.
535fn compare_key_setups(
536 key_id: &KeyId,
537 namespace: Option<&NamespaceId>,
538 key_setup_a: KeySetupComparison,
539 key_setup_b: KeySetupComparison,
540) {
541 let namespace = if let Some(namespace) = namespace {
542 format!(" in namespace \"{namespace}\"")
543 } else {
544 "".to_string()
545 };
546 debug!(
547 "Compare key setup of key \"{key_id}\"{namespace} in {} (A) and {} (B)",
548 key_setup_a.state_origin, key_setup_b.state_origin
549 );
550
551 // Compare key type and warn about mismatches.
552 if key_setup_b.key_type != key_setup_a.key_type {
553 warn!(
554 "Key type mismatch of key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B) backend: {}!",
555 key_setup_a.state_origin,
556 key_setup_a.key_type,
557 key_setup_b.state_origin,
558 key_setup_b.key_type
559 );
560 }
561
562 // Compare key mechanisms and warn about mismatches.
563 if key_setup_b.key_mechanisms != key_setup_a.key_mechanisms {
564 warn!(
565 "Key mechanisms mismatch for key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B): {}!",
566 key_setup_a.state_origin,
567 key_setup_a
568 .key_mechanisms
569 .iter()
570 .map(|mechanism| mechanism.to_string())
571 .collect::<Vec<String>>()
572 .join(", "),
573 key_setup_b.state_origin,
574 key_setup_b
575 .key_mechanisms
576 .iter()
577 .map(|mechanism| mechanism.to_string())
578 .collect::<Vec<String>>()
579 .join(", "),
580 );
581 }
582}
583
584/// Sets up all system-wide keys.
585///
586/// Creates any missing keys and adds the configured tags for all of them.
587/// If keys exist already, deletes all tags and adds the configured ones for them.
588///
589/// # Note
590///
591/// It is assumed that all required _R-Administrators_ have already been set up (see
592/// `add_system_wide_admins`) before calling this function.
593///
594/// This function uses the `nethsm` with the [default
595/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
596///
597/// This function does not fail on mismatching keys, as it is assumed that keys are added
598/// intentionally and should not be deleted or altered.
599/// However, warnings are emitted if an existing key has a mismatching [`KeyType`] or
600/// [`KeyMechanisms`][`KeyMechanism`] from what is configured in the Signstar configuration file.
601///
602/// # Errors
603///
604/// Returns an error if
605///
606/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
607/// - the list of available keys on the NetHSM backend cannot be retrieved,
608/// - information about a single key cannot be retrieved from the NetHSM backend,
609/// - if a tag cannot be removed from an existing key,
610/// - if a tag cannot be added to an existing key,
611/// - or if a missing key cannot be created.
612fn add_system_wide_keys(
613 nethsm: &NetHsm,
614 admin_credentials: &NetHsmAdminCredentials,
615 user_mappings: &[&NetHsmUserMapping],
616) -> Result<(), crate::Error> {
617 debug!(
618 "Setup system-wide cryptographic keys on NetHSM backend at {}",
619 nethsm.get_url()
620 );
621
622 // Use the default R-Administrator for authentication to the backend by default.
623 let default_admin = &admin_credentials.get_default_administrator()?.name;
624 nethsm.use_credentials(default_admin)?;
625
626 let available_keys = nethsm.get_keys(None)?;
627
628 for user_mapping in user_mappings {
629 let Some(user_key_data) =
630 user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::SystemWide)
631 else {
632 // We are only interested in mappings that define key data.
633 continue;
634 };
635
636 if available_keys.contains(user_key_data.key_id) {
637 // Retrieve information about the key.
638 let info = nethsm.get_key(user_key_data.key_id)?;
639
640 // Compare the key setups.
641 compare_key_setups(
642 user_key_data.key_id,
643 None,
644 KeySetupComparison {
645 state_origin: StateOrigin::Config,
646 key_type: user_key_data.key_setup.key_type(),
647 key_mechanisms: HashSet::from_iter(
648 user_key_data.key_setup.key_mechanisms().to_vec(),
649 ),
650 },
651 KeySetupComparison {
652 state_origin: StateOrigin::Backend,
653 key_type: info
654 .r#type
655 .try_into()
656 .map_err(nethsm::Error::SignstarCryptoKey)?,
657 key_mechanisms: info
658 .mechanisms
659 .iter()
660 .filter_map(|mechanism| mechanism.try_into().ok())
661 .collect(),
662 },
663 );
664
665 // Remove all existing tags.
666 if let Some(available_tags) = info.restrictions.tags {
667 for available_tag in available_tags {
668 nethsm.delete_key_tag(user_key_data.key_id, available_tag.as_str())?;
669 }
670 }
671 // Add the required tag to the key.
672 nethsm.add_key_tag(user_key_data.key_id, user_key_data.tag)?;
673 } else {
674 // Add the key, including the required tag.
675 nethsm.generate_key(
676 user_key_data.key_setup.key_type(),
677 user_key_data.key_setup.key_mechanisms().to_vec(),
678 user_key_data.key_setup.key_length(),
679 Some(user_key_data.key_id.clone()),
680 Some(vec![user_key_data.tag.to_string()]),
681 )?;
682 }
683 }
684
685 Ok(())
686}
687
688/// Sets up all namespaced keys and tags them.
689///
690/// Creates any missing keys and adds the configured tags for all of them.
691/// If keys exist already, deletes all tags and adds the configured ones for them.
692///
693/// # Note
694///
695/// It is assumed that _N-Administrators_ have already been set up, before calling
696/// this function (see `add_namespace_admins`).
697///
698/// This function uses the `nethsm` with the [default
699/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
700/// namespace-specific _N-Administrator_ for individual operations.
701/// If this function succeeds, the `nethsm` is guaranteed to use the [default
702/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
703/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
704///
705/// This function does not fail on mismatching keys, as it is assumed that keys are added
706/// intentionally and should not be deleted/altered.
707/// However, warnings are emitted if an existing key has a mismatching key type or key mechanisms
708/// from what is configured in the Signstar configuration file.
709///
710/// Opposite to the behavior of `add_system_wide_keys`, this function does not delete any tags from
711/// keys.
712/// This is due to [a bug in the NetHSM firmware], which leads to a crash when adding a tag to a
713/// key, trying to remove and then re-adding it again.
714///
715/// # Errors
716///
717/// Returns an error if
718///
719/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
720/// - retrieving the list of available users from the NetHSM backend fails,
721/// - a namespaced user mapped to a key is not in a namespace,
722/// - no usable *N-Administrator* for a namespace is known,
723/// - the available keys in the namespace cannot be retrieved,
724/// - information about a specific key in the namespace cannot be retrieved,
725/// - a tag cannot be added to an already existing key,
726/// - a new key cannot be generated,
727/// - or using the default system-wide administrator again fails.
728///
729/// [a bug in the NetHSM firmware]: https://github.com/Nitrokey/nethsm/issues/13
730fn add_namespaced_keys(
731 nethsm: &NetHsm,
732 admin_credentials: &NetHsmAdminCredentials,
733 user_mappings: &[&NetHsmUserMapping],
734) -> Result<(), crate::Error> {
735 debug!(
736 "Setup namespaced cryptographic keys on NetHSM backend at {}",
737 nethsm.get_url()
738 );
739
740 // Use the default R-Administrator for authentication to the backend by default.
741 let default_admin = &admin_credentials.get_default_administrator()?.name;
742 nethsm.use_credentials(default_admin)?;
743
744 let available_users = nethsm.get_users()?;
745
746 let all_user_key_data = user_mappings
747 .iter()
748 .filter_map(|user_mapping| {
749 user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::Namespaced)
750 })
751 .collect::<Vec<_>>();
752
753 for user_key_data in all_user_key_data {
754 debug!(
755 "Set up key \"{}\" with tag {} for user {}",
756 user_key_data.key_id, user_key_data.tag, user_key_data.user
757 );
758
759 // Extract the namespace from the user or return an error.
760 let Some(namespace) = user_key_data.user.namespace() else {
761 // Note: Returning this error is not really possible, as we are explicitly
762 // requesting tuples of namespaced user, key setup and tag.
763 return Err(Error::NamespaceUserNoNamespace {
764 user: user_key_data.user.clone(),
765 }
766 .into());
767 };
768
769 // Select the first available N-Administrator credentials for interacting with the
770 // NetHSM backend.
771 nethsm.use_credentials(&get_first_available_namespace_admin(
772 nethsm,
773 admin_credentials,
774 &available_users,
775 namespace,
776 )?)?;
777
778 let available_keys = nethsm.get_keys(None)?;
779
780 if available_keys.contains(user_key_data.key_id) {
781 let key_info = nethsm.get_key(user_key_data.key_id)?;
782
783 // Compare the key setups.
784 compare_key_setups(
785 user_key_data.key_id,
786 Some(namespace),
787 KeySetupComparison {
788 state_origin: StateOrigin::Config,
789 key_type: user_key_data.key_setup.key_type(),
790 key_mechanisms: HashSet::from_iter(
791 user_key_data.key_setup.key_mechanisms().to_vec(),
792 ),
793 },
794 KeySetupComparison {
795 state_origin: StateOrigin::Backend,
796 key_type: key_info
797 .r#type
798 .try_into()
799 .map_err(nethsm::Error::SignstarCryptoKey)?,
800 key_mechanisms: key_info
801 .mechanisms
802 .iter()
803 .filter_map(|mechanism| mechanism.try_into().ok())
804 .collect(),
805 },
806 );
807
808 // If there are tags already, check if the tag we are looking for is already set and
809 // if so, skip to the next key.
810 if let Some(available_tags) = key_info.restrictions.tags {
811 debug!(
812 "Available tags for key \"{}\" in namespace {namespace}: {}",
813 user_key_data.key_id,
814 available_tags.join(", ")
815 );
816 // NOTE: If the required tag is already set, continue to the next key.
817 // Without this we otherwise trigger a bug in the NetHSM firmware which
818 // breaks the connection after re-adding the tag for the key further down.
819 // (i.e. "Bad Status: HTTP version did not start with HTTP/")
820 // See https://github.com/Nitrokey/nethsm/issues/13 for details.
821 if available_tags.len() == 1
822 && available_tags
823 .iter()
824 .find(|tag| tag.as_str() == user_key_data.tag)
825 .is_some()
826 {
827 continue;
828 }
829 }
830
831 // Add the tag to the key.
832 nethsm.add_key_tag(user_key_data.key_id, user_key_data.tag)?;
833 } else {
834 // Add the key, including the required tag.
835 nethsm.generate_key(
836 user_key_data.key_setup.key_type(),
837 user_key_data.key_setup.key_mechanisms().to_vec(),
838 user_key_data.key_setup.key_length(),
839 Some(user_key_data.key_id.clone()),
840 Some(vec![user_key_data.tag.to_string()]),
841 )?;
842 }
843 }
844
845 // Always use the default R-Administrator again.
846 nethsm.use_credentials(default_admin)?;
847
848 Ok(())
849}
850
851/// Adds OpenPGP certificates for system-wide keys that are used for OpenPGP signing.
852///
853/// # Note
854///
855/// It is assumed that the [default
856/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all system-wide keys
857/// and all system-wide non-administrative users are already set up, before calling this function
858/// (see `add_system_wide_admins`, `add_system_wide_keys` and `add_non_administrative_users`,
859/// respectively).
860///
861/// This function uses the `nethsm` with the [default
862/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
863/// system-wide, non-administrative user for individual operations.
864/// If this function succeeds, the `nethsm` is guaranteed to use the [default
865/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
866/// If this function fails, the `nethsm` may still use a system-wide, non-administrative user.
867///
868/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
869/// inconsistencies between signatures created with a previous version of a certificate and those
870/// created with a new version of the certificate, which is hard to debug.
871///
872/// # Errors
873///
874/// Returns an error if
875///
876/// - using the default *R-Administrator* fails,
877/// - retrieving the names of all system-wide users fails,
878/// - retrieving the names of all system-wide keys fails,
879/// - a user used for OpenPGP signing does not exist,
880/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
881/// - a user used for OpenPGP signing does not have a required tag,
882/// - a key used for OpenPGP signing does not exist,
883/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
884/// - a key used for OpenPGP signing does not have a required tag,
885/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
886/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
887/// create an OpenPGP certificate for the key,
888/// - or the default *R-Administrator* cannot be used to import the generated OpenPGP certificate
889/// for the key.
890fn add_system_wide_openpgp_certificates(
891 nethsm: &NetHsm,
892 admin_credentials: &NetHsmAdminCredentials,
893 user_mappings: &[&NetHsmUserMapping],
894) -> Result<(), crate::Error> {
895 debug!(
896 "Setup OpenPGP certificates for system-wide cryptographic keys on NetHSM backend at {}",
897 nethsm.get_url()
898 );
899
900 // Use the default R-Administrator for authentication to the backend by default.
901 let default_admin = &admin_credentials.get_default_administrator()?.name;
902 nethsm.use_credentials(default_admin)?;
903
904 let available_users = nethsm.get_users()?;
905
906 let all_user_key_data = user_mappings
907 .iter()
908 .filter_map(|user_mapping| {
909 user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::SystemWide)
910 })
911 .collect::<Vec<_>>();
912
913 for user_key_data in all_user_key_data {
914 // Get OpenPGP User IDs and version or continue to the next user/key setup if the
915 // mapping is not used for OpenPGP signing.
916 let CryptographicKeyContext::OpenPgp { user_ids, version } =
917 user_key_data.key_setup.key_context()
918 else {
919 debug!(
920 "Skip creating an OpenPGP certificate for the key \"{}\" used by user \"{}\" as it is not used in an OpenPGP context.",
921 user_key_data.key_id, user_key_data.user,
922 );
923 continue;
924 };
925
926 // Ensure the targeted user exists.
927 if !available_users.contains(user_key_data.user) {
928 return Err(Error::UserMissing {
929 user_id: user_key_data.user.clone(),
930 }
931 .into());
932 }
933 // Ensure the required tag is assigned to the targeted user.
934 if nethsm
935 .get_user_tags(user_key_data.user)?
936 .iter()
937 .find(|tag| tag.as_str() == user_key_data.tag)
938 .is_none()
939 {
940 return Err(Error::UserMissingTag {
941 user_id: user_key_data.user.clone(),
942 tag: user_key_data.tag.to_string(),
943 }
944 .into());
945 }
946
947 let available_keys = nethsm.get_keys(None)?;
948
949 // Ensure the targeted key exists.
950 if !available_keys.contains(user_key_data.key_id) {
951 return Err(Error::KeyMissing {
952 key_id: user_key_data.key_id.clone(),
953 }
954 .into());
955 }
956 // Ensure the required tag is assigned to the targeted key.
957 if !nethsm
958 .get_key(user_key_data.key_id)?
959 .restrictions
960 .tags
961 .is_some_and(|tags| {
962 tags.iter()
963 .find(|tag| tag.as_str() == user_key_data.tag)
964 .is_some()
965 })
966 {
967 return Err(Error::KeyIsMissingTag {
968 key_id: user_key_data.key_id.clone(),
969 tag: user_key_data.tag.to_string(),
970 }
971 .into());
972 }
973
974 // Create the OpenPGP certificate if it does not exist yet.
975 if nethsm.get_key_certificate(user_key_data.key_id)?.is_none() {
976 // Ensure the first OpenPGP User ID exists.
977 let Some(user_id) = user_ids.first() else {
978 return Err(Error::OpenPgpUserIdMissing {
979 key_id: user_key_data.key_id.clone(),
980 }
981 .into());
982 };
983
984 // Switch to the dedicated user with access to the key to create an OpenPGP
985 // certificate for the key.
986 nethsm.use_credentials(user_key_data.user)?;
987 let data = nethsm.create_openpgp_cert(
988 user_key_data.key_id,
989 OpenPgpKeyUsageFlags::default(),
990 user_id.clone(),
991 Timestamp::now(),
992 *version,
993 )?;
994
995 // Switch back to the default R-Administrator for the import of the OpenPGP
996 // certificate.
997 nethsm.use_credentials(default_admin)?;
998 nethsm.import_key_certificate(user_key_data.key_id, data)?;
999 }
1000 }
1001
1002 // Always use the default R-Administrator again.
1003 nethsm.use_credentials(default_admin)?;
1004
1005 Ok(())
1006}
1007
1008/// Adds OpenPGP certificates for namespaced keys that are used for OpenPGP signing.
1009///
1010/// # Note
1011///
1012/// It is assumed that the [default
1013/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all namespaced keys,
1014/// all _N-Administrators_ and all namespaced non-administrative users are already set up, before
1015/// calling this function (see `add_system_wide_admins`, `add_namespaced_keys`,
1016/// `add_namespace_admins` and `add_namespaced_non_administrative_users`, respectively).
1017///
1018/// This function uses the `nethsm` with the [default
1019/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1020/// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1021/// If this function succeeds, the `nethsm` is guaranteed to use the [default
1022/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1023/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1024/// non-administrative user.
1025///
1026/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
1027/// inconsistencies between signatures created with a previous version of a certificate and those
1028/// created with a new version of the certificate, which is hard to debug.
1029///
1030/// # Errors
1031///
1032/// Returns an error if
1033///
1034/// - using the default *R-Administrator* fails,
1035/// - retrieving the names of all users fails,
1036/// - a namespaced user is not in a namespace,
1037/// - no usable *N-Administrator* for a namespace is known,
1038/// - a user used for OpenPGP signing does not exist,
1039/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
1040/// - a user used for OpenPGP signing does not have a required tag,
1041/// - retrieving the names of all keys in a namespace fails,
1042/// - a key used for OpenPGP signing does not exist,
1043/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
1044/// - a key used for OpenPGP signing does not have a required tag,
1045/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
1046/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
1047/// create an OpenPGP certificate for the key,
1048/// - or the *N-Administrator* cannot be used to import the generated OpenPGP certificate for the
1049/// key.
1050fn add_namespaced_openpgp_certificates(
1051 nethsm: &NetHsm,
1052 admin_credentials: &NetHsmAdminCredentials,
1053 user_mappings: &[&NetHsmUserMapping],
1054) -> Result<(), crate::Error> {
1055 debug!(
1056 "Setup OpenPGP certificates for namespaced cryptographic keys on NetHSM backend at {}",
1057 nethsm.get_url()
1058 );
1059
1060 // Use the default R-Administrator for authentication to the backend by default.
1061 let default_admin = &admin_credentials.get_default_administrator()?.name;
1062 nethsm.use_credentials(default_admin)?;
1063
1064 let available_users = nethsm.get_users()?;
1065
1066 let nethsm_user_key_data_list = user_mappings
1067 .iter()
1068 .filter_map(|user_mapping| {
1069 let Some(user_key_data) =
1070 user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::Namespaced)
1071 else {
1072 // We are only interested in mappings that define key data.
1073 return None;
1074 };
1075 // We are only interested in mappings that define OpenPGP key data.
1076 if !matches!(
1077 user_key_data.key_setup.key_context(),
1078 CryptographicKeyContext::OpenPgp { .. }
1079 ) {
1080 return None;
1081 }
1082
1083 Some(user_key_data)
1084 })
1085 .collect::<Vec<_>>();
1086
1087 for user_key_data in nethsm_user_key_data_list {
1088 // Get OpenPGP User IDs and version or continue to the next user/key setup if the
1089 // mapping is not used for OpenPGP signing.
1090 let CryptographicKeyContext::OpenPgp { user_ids, version } =
1091 user_key_data.key_setup.key_context()
1092 else {
1093 continue;
1094 };
1095
1096 // Extract the namespace from the user.
1097 let Some(namespace) = user_key_data.user.namespace() else {
1098 // Note: Returning this error is not really possible, as we are explicitly
1099 // requesting tuples of namespaced user, key setup and tag.
1100 return Err(Error::NamespaceUserNoNamespace {
1101 user: user_key_data.user.clone(),
1102 }
1103 .into());
1104 };
1105
1106 // Select the first available N-Administrator credentials for interacting with the
1107 // NetHSM backend.
1108 let admin = get_first_available_namespace_admin(
1109 nethsm,
1110 admin_credentials,
1111 &available_users,
1112 namespace,
1113 )?;
1114 nethsm.use_credentials(&admin)?;
1115
1116 // Ensure the targeted user exists.
1117 if !available_users.contains(user_key_data.user) {
1118 return Err(Error::NamespaceUserMissing {
1119 user: user_key_data.user.clone(),
1120 namespace: namespace.clone(),
1121 }
1122 .into());
1123 }
1124 // Ensure the required tag is assigned to the targeted user.
1125 let user_tags = nethsm.get_user_tags(user_key_data.user)?;
1126 if user_tags
1127 .iter()
1128 .find(|tag| tag.as_str() == user_key_data.tag)
1129 .is_none()
1130 {
1131 return Err(Error::NamespaceUserMissingTag {
1132 user: user_key_data.user.clone(),
1133 namespace: namespace.clone(),
1134 tag: user_key_data.tag.to_string(),
1135 }
1136 .into());
1137 }
1138
1139 let available_keys = nethsm.get_keys(None)?;
1140
1141 // Ensure the targeted key exists.
1142 if !available_keys.contains(user_key_data.key_id) {
1143 return Err(Error::NamespaceKeyMissing {
1144 key_id: user_key_data.key_id.clone(),
1145 namespace: namespace.clone(),
1146 }
1147 .into());
1148 }
1149 // Ensure the required tag is assigned to the targeted key.
1150 let pubkey = nethsm.get_key(user_key_data.key_id)?;
1151 if !pubkey.restrictions.tags.is_some_and(|tags| {
1152 tags.iter()
1153 .find(|tag| tag.as_str() == user_key_data.tag)
1154 .is_some()
1155 }) {
1156 return Err(Error::NamespaceKeyMissesTag {
1157 key_id: user_key_data.key_id.clone(),
1158 namespace: namespace.clone(),
1159 tag: user_key_data.tag.to_string(),
1160 }
1161 .into());
1162 }
1163
1164 // Create the OpenPGP certificate if it does not exist yet.
1165 if nethsm.get_key_certificate(user_key_data.key_id)?.is_none() {
1166 // Ensure the first OpenPGP User ID exists.
1167 let Some(user_id) = user_ids.first() else {
1168 return Err(Error::NamespaceOpenPgpUserIdMissing {
1169 key_id: user_key_data.key_id.clone(),
1170 namespace: namespace.clone(),
1171 }
1172 .into());
1173 };
1174
1175 // Switch to the dedicated user with access to the key to create an OpenPGP
1176 // certificate for the key.
1177 nethsm.use_credentials(user_key_data.user)?;
1178 let data = nethsm.create_openpgp_cert(
1179 user_key_data.key_id,
1180 OpenPgpKeyUsageFlags::default(),
1181 user_id.clone(),
1182 Timestamp::now(),
1183 *version,
1184 )?;
1185
1186 // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1187 nethsm.use_credentials(&admin)?;
1188 nethsm.import_key_certificate(user_key_data.key_id, data)?;
1189 }
1190 }
1191
1192 // Always use the default R-Administrator again.
1193 nethsm.use_credentials(default_admin)?;
1194
1195 Ok(())
1196}
1197
1198/// A NetHSM backend that provides full control over its data.
1199///
1200/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1201/// by the tracked [`NetHsmAdminCredentials`] and [`Config`].
1202#[derive(Debug)]
1203pub struct NetHsmBackend<'a, 'b> {
1204 nethsm: NetHsm,
1205 admin_credentials: &'a NetHsmAdminCredentials,
1206 nethsm_config: &'b NetHsmConfig,
1207}
1208
1209impl<'a, 'b> NetHsmBackend<'a, 'b> {
1210 /// Creates a new [`NetHsmBackend`].
1211 ///
1212 /// Returns `Some(None)` if `signstar_config` contains no [`NetHsmConfig`].
1213 ///
1214 /// # Errors
1215 ///
1216 /// Returns an error if
1217 ///
1218 /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1219 /// - or retrieving the default administrator from the `admin_credentials` fails.
1220 ///
1221 /// # Examples
1222 ///
1223 /// ```
1224 /// use std::{collections::BTreeSet, num::NonZeroUsize};
1225 ///
1226 /// use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1227 /// use signstar_config::{
1228 /// config::{ConfigBuilder, SystemConfig, SystemUserMapping},
1229 /// nethsm::{NetHsmAdminCredentials, NetHsmBackend, NetHsmConfig, NetHsmMetricsUsers, NetHsmUserMapping},
1230 /// };
1231 /// use signstar_crypto::{
1232 /// AdministrativeSecretHandling,
1233 /// NonAdministrativeSecretHandling,
1234 /// key::{CryptographicKeyContext, KeyMechanism, KeyType, SigningKeySetup, SignatureType},
1235 /// openpgp::OpenPgpUserIdList,
1236 /// };
1237 ///
1238 /// # fn main() -> testresult::TestResult {
1239 /// // The NetHSM connection.
1240 /// let nethsm = NetHsm::new(
1241 /// Connection::new(
1242 /// "https://example.org/api/v1".try_into()?,
1243 /// ConnectionSecurity::Unsafe,
1244 /// ),
1245 /// None,
1246 /// None,
1247 /// None,
1248 /// )?;
1249 /// // The administrative credentials.
1250 /// let admin_credentials = NetHsmAdminCredentials::new(
1251 /// 1,
1252 /// "backup-passphrase".parse()?,
1253 /// "unlock-passphrase".parse()?,
1254 /// vec![FullCredentials::new(
1255 /// "admin".parse()?,
1256 /// "admin-passphrase".parse()?,
1257 /// )],
1258 /// vec![FullCredentials::new(
1259 /// "ns1~admin".parse()?,
1260 /// "ns1-admin-passphrase".parse()?,
1261 /// )],
1262 /// )?;
1263 /// // The Signstar config.
1264 /// let signstar_config = ConfigBuilder::new(SystemConfig::new(
1265 /// 1,
1266 /// AdministrativeSecretHandling::ShamirsSecretSharing {
1267 /// number_of_shares: NonZeroUsize::new(3).expect("3 is larger than 0"),
1268 /// threshold: NonZeroUsize::new(2).expect("2 is larger than 0"),
1269 /// },
1270 /// NonAdministrativeSecretHandling::SystemdCreds,
1271 /// BTreeSet::from_iter([
1272 /// SystemUserMapping::ShareHolder {
1273 /// system_user: "share-holder1".parse()?,
1274 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAN54Gd1jMz+yNDjBRwX1SnOtWuUsVF64RJIeYJ8DI7b user@host".parse()?,
1275 /// },
1276 /// SystemUserMapping::ShareHolder {
1277 /// system_user: "share-holder2".parse()?,
1278 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
1279 /// },
1280 /// SystemUserMapping::ShareHolder {
1281 /// system_user: "share-holder3".parse()?,
1282 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWqWyMCk5BdSl1c3KYoLEokKr7qNVPbI1IbBhgEBQj5 user@host".parse()?
1283 /// },
1284 /// SystemUserMapping::WireGuardDownload {
1285 /// system_user: "wireguard-downloader".parse()?,
1286 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1287 /// },
1288 /// ]),
1289 /// )?)
1290 /// .set_nethsm_config(NetHsmConfig::new(
1291 /// BTreeSet::from_iter([
1292 /// Connection::new("https:///nethsm1.example.org/".parse()?, ConnectionSecurity::Unsafe),
1293 /// Connection::new("https:///nethsm2.example.org/".parse()?, ConnectionSecurity::Unsafe),
1294 /// ]),
1295 /// BTreeSet::from_iter([
1296 /// NetHsmUserMapping::Admin("admin".parse()?),
1297 /// NetHsmUserMapping::Backup{
1298 /// backend_user: "backup".parse()?,
1299 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host".parse()?,
1300 /// system_user: "nethsm-backup-user".parse()?,
1301 /// },
1302 /// NetHsmUserMapping::HermeticMetrics {
1303 /// backend_users: NetHsmMetricsUsers::new("hermeticmetrics".parse()?, vec!["hermetickeymetrics".parse()?])?,
1304 /// system_user: "nethsm-hermetic-metrics-user".parse()?,
1305 /// },
1306 /// NetHsmUserMapping::Metrics {
1307 /// backend_users: NetHsmMetricsUsers::new("metrics".parse()?, vec!["keymetrics".parse()?])?,
1308 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host".parse()?,
1309 /// system_user: "nethsm-metrics-user".parse()?,
1310 /// },
1311 /// NetHsmUserMapping::Signing {
1312 /// backend_user: "signing".parse()?,
1313 /// signing_key_id: "signing1".parse()?,
1314 /// key_setup: SigningKeySetup::new(
1315 /// KeyType::Curve25519,
1316 /// vec![KeyMechanism::EdDsaSignature],
1317 /// None,
1318 /// SignatureType::EdDsa,
1319 /// CryptographicKeyContext::OpenPgp {
1320 /// user_ids: OpenPgpUserIdList::new(vec![
1321 /// "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1322 /// ])?,
1323 /// version: "v4".parse()?,
1324 /// },
1325 /// )?,
1326 /// ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host".parse()?,
1327 /// system_user: "nethsm-signing-user".parse()?,
1328 /// tag: "signing1".to_string(),
1329 /// }
1330 /// ]),
1331 /// )?)
1332 /// .finish()?;
1333 ///
1334 /// let nethsm_backend = NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config)?;
1335 /// # Ok(())
1336 /// # }
1337 /// ```
1338 pub fn new(
1339 nethsm: NetHsm,
1340 admin_credentials: &'a NetHsmAdminCredentials,
1341 signstar_config: &'b Config,
1342 ) -> Result<Option<Self>, crate::Error> {
1343 debug!(
1344 "Create a new NetHSM backend for Signstar config at {}",
1345 nethsm.get_url()
1346 );
1347
1348 let Some(nethsm_config) = signstar_config.nethsm() else {
1349 return Ok(None);
1350 };
1351
1352 // Ensure that the iterations of administrative credentials and signstar config match.
1353 if admin_credentials.get_iteration() != signstar_config.system().iteration() {
1354 return Err(Error::IterationMismatch {
1355 admin_creds: admin_credentials.get_iteration(),
1356 signstar_config: signstar_config.system().iteration(),
1357 }
1358 .into());
1359 }
1360
1361 // Add all available system-wide Administrators for the connection
1362 for user in admin_credentials.administrators_in_config(nethsm_config) {
1363 nethsm.add_credentials(user.into());
1364 }
1365 // Add all available namespace Administrators for the connection
1366 for user in admin_credentials.namespace_administrators_in_config(nethsm_config) {
1367 nethsm.add_credentials(user.into());
1368 }
1369 // Use the default administrator
1370 nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1371
1372 Ok(Some(Self {
1373 nethsm,
1374 admin_credentials,
1375 nethsm_config,
1376 }))
1377 }
1378
1379 /// Returns a reference to the tracked [`NetHsm`].
1380 pub fn nethsm(&self) -> &NetHsm {
1381 &self.nethsm
1382 }
1383
1384 /// Unlocks a locked [`NetHsm`] backend.
1385 pub(crate) fn unlock_nethsm(&self) -> Result<(), crate::Error> {
1386 Ok(self.nethsm.unlock(Passphrase::new(
1387 self.admin_credentials.get_unlock_passphrase().into(),
1388 ))?)
1389 }
1390
1391 /// Retrieves the state for all users on the [`NetHsm`] backend.
1392 ///
1393 /// # Note
1394 ///
1395 /// Uses the `nethsm` with the [default
1396 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
1397 ///
1398 /// # Errors
1399 ///
1400 /// Returns an error if
1401 ///
1402 /// - using the credentials of the default *R-Administrator* fails,
1403 /// - retrieving all user names of the NetHSM backend fails,
1404 /// - retrieving information about a specific NetHSM user fails,
1405 /// - or retrieving the tags of an *Operator* user fails.
1406 pub(crate) fn user_states(&self) -> Result<Vec<UserState>, crate::Error> {
1407 info!(
1408 "Retrieve all user states from NetHSM backend at \"{}\"",
1409 self.nethsm.get_url()
1410 );
1411 // Use the default R-Administrator.
1412 self.nethsm
1413 .use_credentials(&self.admin_credentials.get_default_administrator()?.name)?;
1414
1415 let users = {
1416 let mut users: Vec<UserState> = Vec::new();
1417
1418 for user_id in self.nethsm.get_users()? {
1419 let user_data = self.nethsm.get_user(&user_id)?;
1420 let tag = {
1421 // Only Operator users can have tags assigned to them.
1422 if user_data.role == UserRole::Operator.into() {
1423 let user_tags = self.nethsm.get_user_tags(&user_id)?;
1424 match user_tags.len() {
1425 0 => None,
1426 1 => user_tags.first().cloned(),
1427 number => {
1428 return Err(
1429 Error::UserUnexpectedNumberOfTags { user_id, number }.into()
1430 );
1431 }
1432 }
1433 } else {
1434 None
1435 }
1436 };
1437
1438 users.push(UserState {
1439 name: user_id,
1440 role: user_data.role.into(),
1441 tag,
1442 });
1443 }
1444
1445 users
1446 };
1447
1448 Ok(users)
1449 }
1450
1451 /// Retrieves the state of a key certificate on the [`NetHsm`] backend.
1452 ///
1453 /// Key certificates may be retrieved for system-wide keys or namespaced keys.
1454 /// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1455 /// retrieved.
1456 ///
1457 /// # Note
1458 ///
1459 /// It is assumed that the current credentials for the `nethsm` provide access to the key
1460 /// certificate of key `key_id`.
1461 fn key_certificate_state(
1462 &self,
1463 key_id: &KeyId,
1464 namespace: Option<&NamespaceId>,
1465 ) -> KeyCertificateState {
1466 // Provide a dedicated string for log messages in case a namespace is used.
1467 let namespace = if let Some(namespace) = namespace {
1468 format!(" in namespace \"{namespace}\"")
1469 } else {
1470 "".to_string()
1471 };
1472 info!(
1473 "Retrieve the key certificate state for key {key_id}{namespace} from NetHSM backend at \"{}\"",
1474 self.nethsm.get_url()
1475 );
1476
1477 match self.nethsm.get_key_certificate(key_id) {
1478 Ok(Some(key_cert)) => {
1479 let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1480 Ok((public_key, _armor_header)) => public_key,
1481 Err(error) => {
1482 let message = format!(
1483 "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1484 );
1485 debug!("{message}");
1486 return KeyCertificateState::NotAnOpenPgpCertificate { message };
1487 }
1488 };
1489
1490 match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1491 Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1492 Err(error) => {
1493 let message = format!(
1494 "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1495 );
1496 debug!("{message}");
1497 KeyCertificateState::NotACryptographicKeyContext { message }
1498 }
1499 }
1500 }
1501 Ok(None) => KeyCertificateState::Empty,
1502 Err(error) => {
1503 let message = error.to_string();
1504 debug!("{message}");
1505 KeyCertificateState::Error { message }
1506 }
1507 }
1508 }
1509
1510 /// Retrieves the state for all keys on the [`NetHsm`] backend.
1511 ///
1512 /// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1513 /// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1514 ///
1515 /// # Note
1516 ///
1517 /// This function uses the `nethsm` with the [default
1518 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1519 /// namespace-specific _N-Administrator_ for individual operations.
1520 /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1521 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1522 /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1523 ///
1524 ///
1525 /// # Errors
1526 ///
1527 /// Returns an error if
1528 ///
1529 /// - using the default *R-Administrator* for authentication against the backend fails,
1530 /// - retrieving the names of all system-wide keys on the backend fails,
1531 /// - retrieving information on a specific system-wide key on the backend fails,
1532 /// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1533 /// - using the credentials of an *N-Administrator* fails,
1534 /// - retrieving the names of all namespaced keys on the backend fails,
1535 /// - or retrieving information on a specific namespaced key on the backend fails.
1536 pub(crate) fn key_states(&self) -> Result<Vec<KeyState>, crate::Error> {
1537 info!(
1538 "Retrieve all key states from NetHSM backend at \"{}\"",
1539 self.nethsm.get_url()
1540 );
1541 // Use the default administrator
1542 let default_admin = &self.admin_credentials.get_default_administrator()?.name;
1543 self.nethsm.use_credentials(default_admin)?;
1544
1545 let mut keys = Vec::new();
1546 // Get the state of system-wide keys.
1547 for key_id in self.nethsm.get_keys(None)? {
1548 let key = self.nethsm.get_key(&key_id)?;
1549 let key_context = self.key_certificate_state(&key_id, None);
1550 let tag = {
1551 let tags = key.restrictions.tags.unwrap_or_default();
1552 if tags.len() > 1 {
1553 return Err(Error::KeyUnexpectedNumberOfTags {
1554 key_id,
1555 number: tags.len(),
1556 }
1557 .into());
1558 }
1559
1560 if let Some(tag) = tags.first() {
1561 tag.clone()
1562 } else {
1563 return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1564 }
1565 };
1566
1567 keys.push(KeyState {
1568 name: key_id,
1569 namespace: None,
1570 tag,
1571 key_type: key
1572 .r#type
1573 .try_into()
1574 .map_err(nethsm::Error::SignstarCryptoKey)?,
1575 mechanisms: key
1576 .mechanisms
1577 .iter()
1578 .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1579 .collect(),
1580 key_cert_state: key_context,
1581 });
1582 }
1583
1584 let mut seen_namespaces = HashSet::new();
1585 // Get the state of namespaced keys.
1586 for user_id in self
1587 .admin_credentials
1588 .get_namespace_administrators()
1589 .iter()
1590 .map(|creds| creds.name.clone())
1591 {
1592 // Extract the namespace of the user and ensure that the namespace exists already.
1593 let Some(namespace) = user_id.namespace() else {
1594 return Err(Error::NamespaceUserNoNamespace {
1595 user: user_id.clone(),
1596 }
1597 .into());
1598 };
1599
1600 // Only extract key information for the namespace if we have not already looked at it.
1601 if seen_namespaces.contains(namespace) {
1602 continue;
1603 }
1604 seen_namespaces.insert(namespace.clone());
1605
1606 self.nethsm.use_credentials(&user_id)?;
1607 for key_id in self.nethsm.get_keys(None)? {
1608 let key = self.nethsm.get_key(&key_id)?;
1609 let key_context = self.key_certificate_state(&key_id, Some(namespace));
1610 let tag = {
1611 let tags = key.restrictions.tags.unwrap_or_default();
1612 if tags.len() > 1 {
1613 return Err(Error::KeyUnexpectedNumberOfTags {
1614 key_id,
1615 number: tags.len(),
1616 }
1617 .into());
1618 }
1619
1620 if let Some(tag) = tags.first() {
1621 tag.clone()
1622 } else {
1623 return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1624 }
1625 };
1626
1627 keys.push(KeyState {
1628 name: key_id,
1629 namespace: Some(namespace.clone()),
1630 tag,
1631 key_type: key
1632 .r#type
1633 .try_into()
1634 .map_err(nethsm::Error::SignstarCryptoKey)?,
1635 mechanisms: key
1636 .mechanisms
1637 .iter()
1638 .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1639 .collect(),
1640 key_cert_state: key_context,
1641 });
1642 }
1643 }
1644
1645 // Always use the default *R-Administrator* again.
1646 self.nethsm.use_credentials(default_admin)?;
1647
1648 Ok(keys)
1649 }
1650
1651 /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1652 /// non-administrative roles.
1653 ///
1654 /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1655 /// Then works down the following list to
1656 ///
1657 /// - create _R-Administrators_,
1658 /// - or set their passphrase if they exist already,
1659 /// - create system-wide keys and add tags to them,
1660 /// - or remove all tags from existing keys and only add the configured tags,
1661 /// - create users in the system-wide, non-administrative roles (i.e.
1662 /// [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1663 /// [`Operator`][`UserRole::Operator`]),
1664 /// - or set their passphrase if they exist already,
1665 /// - create OpenPGP certificates for system-wide keys,
1666 /// - or do nothing if they exist already,
1667 /// - create _N-Administrators_ and their respective namespaces,
1668 /// - or set their passphrase if they exist already,
1669 /// - create namespaced keys and add tags to them,
1670 /// - or remove all tags from existing keys and only add the configured tags,
1671 /// - create users in the namespaced, non-administrative roles (i.e.
1672 /// [`Operator`][`UserRole::Operator`]),
1673 /// - or set their passphrase if they exist already,
1674 /// - and create OpenPGP certificates for namespaced keys,
1675 /// - or do nothing if they exist already.
1676 ///
1677 /// # Note
1678 ///
1679 /// This function uses the `nethsm` with the [default
1680 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1681 /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1682 /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1683 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1684 /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1685 /// non-administrative user.
1686 ///
1687 /// # Errors
1688 ///
1689 /// Returns an error if
1690 ///
1691 /// - retrieving the state of the [`NetHsm`] backend fails,
1692 /// - provisioning an unprovisioned [`NetHsm`] fails,
1693 /// - unlocking a locked [`NetHsm`] backend fails,
1694 /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1695 /// - adding system-wide keys fails,
1696 /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1697 /// [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1698 /// - adding OpenPGP certificates for system-wide keys fails,
1699 /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1700 /// their respective namespace fails,
1701 /// - adding namespaced keys fails,
1702 /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1703 /// - or adding OpenPGP certificates for namespaced keys fails.
1704 pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1705 debug!(
1706 "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1707 self.nethsm.get_url()
1708 );
1709
1710 // Extract user mappings for non-administrative users.
1711 let non_admin_users = self
1712 .nethsm_config
1713 .mappings()
1714 .iter()
1715 .filter(|mapping| !matches!(mapping, NetHsmUserMapping::Admin(..)))
1716 .collect::<Vec<_>>();
1717
1718 match self.nethsm.state()? {
1719 SystemState::Unprovisioned => {
1720 debug!(
1721 "Unprovisioned NetHSM backend detected at {}",
1722 self.nethsm.get_url()
1723 );
1724
1725 self.nethsm.provision(
1726 Passphrase::from_str(self.admin_credentials.get_unlock_passphrase()).map_err(
1727 |source| {
1728 crate::Error::NetHsm(nethsm::Error::SignstarCryptoPassphrase(source))
1729 },
1730 )?,
1731 self.admin_credentials
1732 .get_default_administrator()?
1733 .passphrase
1734 .clone(),
1735 nethsm::Utc::now(),
1736 )?;
1737 }
1738 SystemState::Locked => {
1739 debug!(
1740 "Locked NetHSM backend detected at {}",
1741 self.nethsm.get_url()
1742 );
1743
1744 self.nethsm.unlock(Passphrase::new(
1745 self.admin_credentials.get_unlock_passphrase().into(),
1746 ))?;
1747 }
1748 SystemState::Operational => {
1749 debug!(
1750 "Operational NetHSM backend detected at {}",
1751 self.nethsm.get_url()
1752 );
1753 }
1754 }
1755
1756 // Add any missing users and keys.
1757 add_system_wide_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1758 add_system_wide_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1759 add_non_administrative_users(
1760 &self.nethsm,
1761 self.admin_credentials,
1762 &non_admin_users,
1763 user_credentials,
1764 )?;
1765 add_system_wide_openpgp_certificates(
1766 &self.nethsm,
1767 self.admin_credentials,
1768 &non_admin_users,
1769 )?;
1770 add_namespace_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1771 add_namespaced_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1772 add_namespaced_non_administrative_users(
1773 &self.nethsm,
1774 self.admin_credentials,
1775 &non_admin_users,
1776 user_credentials,
1777 )?;
1778 add_namespaced_openpgp_certificates(
1779 &self.nethsm,
1780 self.admin_credentials,
1781 &non_admin_users,
1782 )?;
1783
1784 Ok(())
1785 }
1786}
1787
1788/// The state of a user in a [`NetHsmBackend`].
1789#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1790pub(crate) struct UserState {
1791 /// The name of the user.
1792 pub name: UserId,
1793 /// The role of the user.
1794 pub role: UserRole,
1795 /// The optional tag assigned to the user.
1796 pub tag: Option<String>,
1797}
1798
1799impl Display for UserState {
1800 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1801 write!(f, "{} (role: {}", self.name, self.role)?;
1802 if let Some(tag) = self.tag.as_ref() {
1803 write!(f, "; tag: {tag}")?;
1804 }
1805 write!(f, ")")?;
1806
1807 Ok(())
1808 }
1809}
1810
1811/// The state of a key in a [`NetHsmBackend`].
1812#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1813pub(crate) struct KeyState {
1814 /// The name of the key.
1815 pub name: KeyId,
1816 /// The optional namespace the key is used in.
1817 pub namespace: Option<NamespaceId>,
1818 /// The tag assigned to the key.
1819 pub tag: String,
1820 /// The key type of the key.
1821 pub key_type: KeyType,
1822 /// The mechanisms supported by the key.
1823 pub mechanisms: Vec<KeyMechanism>,
1824 /// The context in which the key is used.
1825 pub key_cert_state: KeyCertificateState,
1826}
1827
1828impl Display for KeyState {
1829 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1830 write!(f, "{} (", self.name)?;
1831 if let Some(namespace) = self.namespace.as_ref() {
1832 write!(f, "namespace: {namespace}; ")?;
1833 }
1834 write!(f, "tag: {}; ", self.tag)?;
1835 write!(f, "type: {}; ", self.key_type)?;
1836 write!(
1837 f,
1838 "mechanisms: {}; ",
1839 self.mechanisms
1840 .iter()
1841 .map(|mechanism| mechanism.to_string())
1842 .collect::<Vec<String>>()
1843 .join(", ")
1844 )?;
1845 write!(f, "context: {}", self.key_cert_state)?;
1846 write!(f, ")")?;
1847
1848 Ok(())
1849 }
1850}
1851
1852/// The state of a [`NetHsmBackend`].
1853///
1854/// This tracks the available backend users, their roles and assigned tags, and the key setups
1855/// associated with users.
1856#[derive(Debug, Eq, PartialEq)]
1857pub struct NetHsmBackendState {
1858 /// The user states.
1859 pub(crate) user_states: Vec<UserState>,
1860 /// The key states.
1861 pub(crate) key_states: Vec<KeyState>,
1862}
1863
1864impl NetHsmBackendState {
1865 /// The name of the origin for the state.
1866 pub const STATE_NAME: &'static str = "NetHSM backend";
1867}
1868
1869impl StateOriginInfo for NetHsmBackendState {
1870 fn state_name(&self) -> &str {
1871 Self::STATE_NAME
1872 }
1873
1874 fn state_origin(&self) -> StateOrigin {
1875 StateOrigin::Backend
1876 }
1877}
1878
1879impl<'a, 'b> TryFrom<&NetHsmBackend<'a, 'b>> for NetHsmBackendState {
1880 type Error = crate::Error;
1881
1882 /// Creates a new [`NetHsmBackendState`] from a [`NetHsmBackend`].
1883 ///
1884 /// # Note
1885 ///
1886 /// Uses the [`NetHsm`] backend with the [default
1887 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1888 /// namespace-specific _N-Administrator_ for individual operations.
1889 /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1890 /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1891 /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1892 ///
1893 /// # Errors
1894 ///
1895 /// Returns an error if
1896 ///
1897 /// - retrieving the system state of the [`NetHsm`] backend fails,
1898 /// - unlocking a locked [`NetHsm`] backend fails,
1899 /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
1900 fn try_from(value: &NetHsmBackend<'a, 'b>) -> Result<Self, Self::Error> {
1901 debug!(
1902 "Retrieve state of the NetHSM backend at {}",
1903 value.nethsm().get_url()
1904 );
1905
1906 let (user_states, key_states) = match value.nethsm().state()? {
1907 SystemState::Unprovisioned => {
1908 debug!(
1909 "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
1910 value.nethsm().get_url()
1911 );
1912
1913 (Vec::new(), Vec::new())
1914 }
1915 SystemState::Locked => {
1916 debug!(
1917 "Locked NetHSM backend detected at {}",
1918 value.nethsm().get_url()
1919 );
1920
1921 value.unlock_nethsm()?;
1922
1923 let user_states = value.user_states()?;
1924 let key_states = value.key_states()?;
1925
1926 (user_states, key_states)
1927 }
1928 SystemState::Operational => {
1929 debug!(
1930 "Operational NetHSM backend detected at {}",
1931 value.nethsm().get_url()
1932 );
1933
1934 let user_states = value.user_states()?;
1935 let key_states = value.key_states()?;
1936
1937 (user_states, key_states)
1938 }
1939 };
1940
1941 Ok(Self {
1942 user_states,
1943 key_states,
1944 })
1945 }
1946}
1947
1948#[cfg(test)]
1949#[cfg(feature = "_test-helpers")]
1950mod tests {
1951 use log::LevelFilter;
1952 use nethsm::{
1953 Connection,
1954 ConnectionSecurity,
1955 CryptographicKeyContext,
1956 FullCredentials,
1957 NetHsm,
1958 OpenPgpUserIdList,
1959 OpenPgpVersion,
1960 UserRole,
1961 };
1962 use rstest::rstest;
1963 use signstar_common::logging::setup_logging;
1964 use testresult::TestResult;
1965
1966 use super::*;
1967 use crate::test::{ConfigFileConfig, ConfigFileVariant, SystemPrepareConfig};
1968
1969 /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1970 /// [`NetHsmAdminCredentials`] and [`Config`].
1971 #[test]
1972 fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1973 setup_logging(LevelFilter::Debug)?;
1974
1975 let prepare_config = SystemPrepareConfig {
1976 machine_id: false,
1977 credentials_socket: false,
1978 signstar_config: ConfigFileConfig {
1979 location: None,
1980 variant: ConfigFileVariant::OnlyNetHsmBackendAdminPlaintextNonAdminSystemdCreds,
1981 system_user_config: None,
1982 },
1983 };
1984 let signstar_config = prepare_config.signstar_config.variant.to_config()?;
1985
1986 let nethsm = NetHsm::new(
1987 Connection::new(
1988 "https://example.org/api/v1".try_into()?,
1989 ConnectionSecurity::Unsafe,
1990 ),
1991 None,
1992 None,
1993 None,
1994 )?;
1995 // The administrative credentials.
1996 let admin_credentials = NetHsmAdminCredentials::new(
1997 // this is different from the one in the Signstar config.
1998 2,
1999 "backup-passphrase".parse()?,
2000 "unlock-passphrase".parse()?,
2001 vec![FullCredentials::new(
2002 "admin".parse()?,
2003 "admin-passphrase".parse()?,
2004 )],
2005 vec![FullCredentials::new(
2006 "ns1~admin".parse()?,
2007 "ns1-admin-passphrase".parse()?,
2008 )],
2009 )?;
2010 let nethsm_backend_result =
2011 NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
2012
2013 assert!(
2014 nethsm_backend_result.is_err(),
2015 "Test should have failed, but succeeded"
2016 );
2017 assert!(
2018 matches!(
2019 nethsm_backend_result,
2020 Err(crate::Error::NetHsmBackend(Error::IterationMismatch {
2021 admin_creds: _,
2022 signstar_config: _
2023 }))
2024 ),
2025 "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
2026 );
2027
2028 Ok(())
2029 }
2030
2031 /// Ensures that [`UserState::to_string`] shows correctly.
2032 #[rstest]
2033 #[case(
2034 UserState{
2035 name: "testuser".parse()?,
2036 role: UserRole::Operator,
2037 tag: Some("tag1".to_string())
2038 },
2039 "testuser (role: Operator; tag: tag1)",
2040 )]
2041 #[case(
2042 UserState{
2043 name: "testuser".parse()?,
2044 role: UserRole::Operator,
2045 tag: None,
2046 },
2047 "testuser (role: Operator)",
2048 )]
2049 #[case(
2050 UserState{
2051 name: "testuser".parse()?,
2052 role: UserRole::Metrics,
2053 tag: None,
2054 },
2055 "testuser (role: Metrics)",
2056 )]
2057 #[case(
2058 UserState{
2059 name: "testuser".parse()?,
2060 role: UserRole::Backup,
2061 tag: None,
2062 },
2063 "testuser (role: Backup)",
2064 )]
2065 #[case(
2066 UserState{name:
2067 "testuser".parse()?,
2068 role: UserRole::Administrator,
2069 tag: None,
2070 },
2071 "testuser (role: Administrator)",
2072 )]
2073 fn user_state_to_string(#[case] user_state: UserState, #[case] expected: &str) -> TestResult {
2074 setup_logging(LevelFilter::Debug)?;
2075
2076 assert_eq!(user_state.to_string(), expected);
2077 Ok(())
2078 }
2079
2080 /// Ensures that [`KeyState::to_string`] shows correctly.
2081 #[rstest]
2082 #[case::namespaced_key_with_openpgp_v4_cert(
2083 KeyState{
2084 name: "key1".parse()?,
2085 namespace: Some("ns1".parse()?),
2086 tag: "tag1".to_string(),
2087 key_type: KeyType::Curve25519,
2088 mechanisms: vec![KeyMechanism::EdDsaSignature],
2089 key_cert_state: KeyCertificateState::KeyContext(
2090 CryptographicKeyContext::OpenPgp {
2091 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
2092 version: OpenPgpVersion::V4,
2093 })
2094 },
2095 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: OpenPGP (Version: 4; User IDs: \"John Doe <john@example.org>\"))",
2096 )]
2097 #[case::namespaced_key_with_raw_cert(
2098 KeyState{
2099 name: "key1".parse()?,
2100 namespace: Some("ns1".parse()?),
2101 tag: "tag1".to_string(),
2102 key_type: KeyType::Curve25519,
2103 mechanisms: vec![KeyMechanism::EdDsaSignature],
2104 key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2105 },
2106 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2107 )]
2108 #[case::namespaced_key_with_no_cert(
2109 KeyState{
2110 name: "key1".parse()?,
2111 namespace: Some("ns1".parse()?),
2112 tag: "tag1".to_string(),
2113 key_type: KeyType::Curve25519,
2114 mechanisms: vec![KeyMechanism::EdDsaSignature],
2115 key_cert_state: KeyCertificateState::Empty
2116 },
2117 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Empty)",
2118 )]
2119 #[case::namespaced_key_with_cert_error(
2120 KeyState{
2121 name: "key1".parse()?,
2122 namespace: Some("ns1".parse()?),
2123 tag: "tag1".to_string(),
2124 key_type: KeyType::Curve25519,
2125 mechanisms: vec![KeyMechanism::EdDsaSignature],
2126 key_cert_state: KeyCertificateState::Error { message: "the dog ate it".to_string() }
2127 },
2128 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Error retrieving key certificate - the dog ate it)",
2129 )]
2130 #[case::namespaced_key_with_not_a_cert_context(
2131 KeyState{
2132 name: "key1".parse()?,
2133 namespace: Some("ns1".parse()?),
2134 tag: "tag1".to_string(),
2135 key_type: KeyType::Curve25519,
2136 mechanisms: vec![KeyMechanism::EdDsaSignature],
2137 key_cert_state: KeyCertificateState::NotACryptographicKeyContext { message: "failed to convert".to_string() }
2138 },
2139 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not a cryptographic key context - \"failed to convert\")",
2140 )]
2141 #[case::namespaced_key_with_not_an_openpgp_cert(
2142 KeyState{
2143 name: "key1".parse()?,
2144 namespace: Some("ns1".parse()?),
2145 tag: "tag1".to_string(),
2146 key_type: KeyType::Curve25519,
2147 mechanisms: vec![KeyMechanism::EdDsaSignature],
2148 key_cert_state: KeyCertificateState::NotAnOpenPgpCertificate { message: "it's a blob".to_string() }
2149 },
2150 "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not an OpenPGP certificate - \"it's a blob\")",
2151 )]
2152 #[case::system_wide_key_with_no_cert_and_no_tags_and_raw_cert(
2153 KeyState{
2154 name: "key1".parse()?,
2155 namespace: None,
2156 tag: "tag1".to_string(),
2157 key_type: KeyType::Curve25519,
2158 mechanisms: vec![KeyMechanism::EdDsaSignature],
2159 key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2160 },
2161 "key1 (tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2162 )]
2163 fn key_state_to_string(#[case] key_state: KeyState, #[case] expected: &str) -> TestResult {
2164 setup_logging(LevelFilter::Debug)?;
2165
2166 assert_eq!(key_state.to_string(), expected);
2167 Ok(())
2168 }
2169}