Skip to main content

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};
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    SystemState,
28    Timestamp,
29    UserId,
30    UserRole,
31};
32use pgp::composed::{Deserializable, SignedPublicKey};
33
34use crate::{
35    admin_credentials::AdminCredentials,
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::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.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                    .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::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::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.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::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::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.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.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::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::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.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::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.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::SignstarCrypto)?,
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::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::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.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::SignstarCrypto)?,
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::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::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::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.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            if user_ids.as_ref().is_empty() {
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_ids.as_ref(),
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::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::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::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.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            if user_ids.as_ref().is_empty() {
1168                return Err(Error::OpenPgpUserIdMissing {
1169                    key_id: user_key_data.key_id.clone(),
1170                }
1171                .into());
1172            }
1173
1174            // Switch to the dedicated user with access to the key to create an OpenPGP
1175            // certificate for the key.
1176            nethsm.use_credentials(user_key_data.user)?;
1177            let data = nethsm.create_openpgp_cert(
1178                user_key_data.key_id,
1179                OpenPgpKeyUsageFlags::default(),
1180                user_ids.as_ref(),
1181                Timestamp::now(),
1182                *version,
1183            )?;
1184
1185            // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1186            nethsm.use_credentials(&admin)?;
1187            nethsm.import_key_certificate(user_key_data.key_id, data)?;
1188        }
1189    }
1190
1191    // Always use the default R-Administrator again.
1192    nethsm.use_credentials(default_admin)?;
1193
1194    Ok(())
1195}
1196
1197/// A NetHSM backend that provides full control over its data.
1198///
1199/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1200/// by the tracked [`NetHsmAdminCredentials`] and [`Config`].
1201#[derive(Debug)]
1202pub struct NetHsmBackend<'a, 'b> {
1203    nethsm: NetHsm,
1204    admin_credentials: &'a NetHsmAdminCredentials,
1205    nethsm_config: &'b NetHsmConfig,
1206}
1207
1208impl<'a, 'b> NetHsmBackend<'a, 'b> {
1209    /// Creates a new [`NetHsmBackend`].
1210    ///
1211    /// Returns `Some(None)` if `signstar_config` contains no [`NetHsmConfig`].
1212    ///
1213    /// # Errors
1214    ///
1215    /// Returns an error if
1216    ///
1217    /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1218    /// - or retrieving the default administrator from the `admin_credentials` fails.
1219    ///
1220    /// # Examples
1221    ///
1222    /// ```
1223    /// use std::{collections::BTreeSet, num::NonZeroUsize};
1224    ///
1225    /// use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1226    /// use signstar_config::{
1227    ///     config::{ConfigBuilder, SystemConfig, SystemUserMapping},
1228    ///     nethsm::{NetHsmAdminCredentials, NetHsmBackend, NetHsmConfig, NetHsmMetricsUsers, NetHsmUserMapping},
1229    /// };
1230    /// use signstar_crypto::{
1231    ///     AdministrativeSecretHandling,
1232    ///     NonAdministrativeSecretHandling,
1233    ///     key::{CryptographicKeyContext, KeyMechanism, KeyType, SigningKeySetup, SignatureType},
1234    ///     openpgp::OpenPgpUserIdList,
1235    /// };
1236    ///
1237    /// # fn main() -> testresult::TestResult {
1238    /// // The NetHSM connection.
1239    /// let nethsm = NetHsm::new(
1240    ///     Connection::new(
1241    ///         "https://example.org/api/v1".try_into()?,
1242    ///         ConnectionSecurity::Unsafe,
1243    ///     ),
1244    ///     None,
1245    ///     None,
1246    ///     None,
1247    /// )?;
1248    /// // The administrative credentials.
1249    /// let admin_credentials = NetHsmAdminCredentials::new(
1250    ///     1,
1251    ///     "backup-passphrase-really-just-for-testing-i-promise".parse()?,
1252    ///     "unlock-passphrase-really-just-for-testing-i-promise".parse()?,
1253    ///     vec![FullCredentials::new(
1254    ///         "admin".parse()?,
1255    ///         "admin-passphrase-really-just-for-testing-i-promise".parse()?,
1256    ///     )],
1257    ///     vec![FullCredentials::new(
1258    ///         "ns1~admin".parse()?,
1259    ///         "ns1-admin-passphrase-really-just-for-testing-i-promise".parse()?,
1260    ///     )],
1261    /// )?;
1262    /// // The Signstar config.
1263    /// let signstar_config = ConfigBuilder::new(SystemConfig::new(
1264    ///         1,
1265    ///         AdministrativeSecretHandling::ShamirsSecretSharing {
1266    ///             number_of_shares: NonZeroUsize::new(3).expect("3 is larger than 0"),
1267    ///             threshold: NonZeroUsize::new(2).expect("2 is larger than 0"),
1268    ///         },
1269    ///         NonAdministrativeSecretHandling::SystemdCreds,
1270    ///         BTreeSet::from_iter([
1271    ///             SystemUserMapping::ShareHolder {
1272    ///                 system_user: "share-holder1".parse()?,
1273    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAN54Gd1jMz+yNDjBRwX1SnOtWuUsVF64RJIeYJ8DI7b user@host".parse()?,
1274    ///             },
1275    ///             SystemUserMapping::ShareHolder {
1276    ///                 system_user: "share-holder2".parse()?,
1277    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
1278    ///             },
1279    ///             SystemUserMapping::ShareHolder {
1280    ///                 system_user: "share-holder3".parse()?,
1281    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWqWyMCk5BdSl1c3KYoLEokKr7qNVPbI1IbBhgEBQj5 user@host".parse()?
1282    ///             },
1283    ///             SystemUserMapping::WireGuardDownload {
1284    ///                 system_user: "wireguard-downloader".parse()?,
1285    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1286    ///             },
1287    ///         ]),
1288    ///     )?)
1289    ///     .set_nethsm_config(NetHsmConfig::new(
1290    ///         BTreeSet::from_iter([
1291    ///             Connection::new("https:///nethsm1.example.org/".parse()?, ConnectionSecurity::Unsafe),
1292    ///             Connection::new("https:///nethsm2.example.org/".parse()?, ConnectionSecurity::Unsafe),
1293    ///         ]),
1294    ///         BTreeSet::from_iter([
1295    ///             NetHsmUserMapping::Admin("admin".parse()?),
1296    ///             NetHsmUserMapping::Backup{
1297    ///                 backend_user: "backup".parse()?,
1298    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host".parse()?,
1299    ///                 system_user: "nethsm-backup-user".parse()?,
1300    ///             },
1301    ///             NetHsmUserMapping::HermeticMetrics {
1302    ///                 backend_users: NetHsmMetricsUsers::new("hermeticmetrics".parse()?, vec!["hermetickeymetrics".parse()?])?,
1303    ///                 system_user: "nethsm-hermetic-metrics-user".parse()?,
1304    ///             },
1305    ///             NetHsmUserMapping::Metrics {
1306    ///                 backend_users: NetHsmMetricsUsers::new("metrics".parse()?, vec!["keymetrics".parse()?])?,
1307    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host".parse()?,
1308    ///                 system_user: "nethsm-metrics-user".parse()?,
1309    ///             },
1310    ///             NetHsmUserMapping::Signing {
1311    ///                 backend_user: "signing".parse()?,
1312    ///                 signing_key_id: "signing1".parse()?,
1313    ///                 key_setup: SigningKeySetup::new(
1314    ///                     KeyType::Curve25519,
1315    ///                     vec![KeyMechanism::EdDsaSignature],
1316    ///                     None,
1317    ///                     SignatureType::EdDsa,
1318    ///                     CryptographicKeyContext::OpenPgp {
1319    ///                         user_ids: OpenPgpUserIdList::new(vec![
1320    ///                             "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1321    ///                         ])?,
1322    ///                         version: "v4".parse()?,
1323    ///                     },
1324    ///                 )?,
1325    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host".parse()?,
1326    ///                 system_user: "nethsm-signing-user".parse()?,
1327    ///                 tag: "signing1".to_string(),
1328    ///             }
1329    ///         ]),
1330    ///     )?)
1331    ///     .finish()?;
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 NetHsmAdminCredentials,
1340        signstar_config: &'b Config,
1341    ) -> Result<Option<Self>, crate::Error> {
1342        debug!(
1343            "Create a new NetHSM backend for Signstar config at {}",
1344            nethsm.get_url()
1345        );
1346
1347        let Some(nethsm_config) = signstar_config.nethsm() else {
1348            return Ok(None);
1349        };
1350
1351        // Ensure that the iterations of administrative credentials and signstar config match.
1352        if admin_credentials.iteration() != signstar_config.system().iteration() {
1353            return Err(crate::Error::IterationMismatch {
1354                admin_creds: admin_credentials.iteration(),
1355                signstar_config: signstar_config.system().iteration(),
1356            });
1357        }
1358
1359        // Add all available system-wide Administrators for the connection
1360        for user in admin_credentials.administrators_in_config(nethsm_config) {
1361            nethsm.add_credentials(user.into());
1362        }
1363        // Add all available namespace Administrators for the connection
1364        for user in admin_credentials.namespace_administrators_in_config(nethsm_config) {
1365            nethsm.add_credentials(user.into());
1366        }
1367        // Use the default administrator
1368        nethsm.use_credentials(&admin_credentials.default_administrator()?.name)?;
1369
1370        Ok(Some(Self {
1371            nethsm,
1372            admin_credentials,
1373            nethsm_config,
1374        }))
1375    }
1376
1377    /// Returns a reference to the tracked [`NetHsm`].
1378    pub fn nethsm(&self) -> &NetHsm {
1379        &self.nethsm
1380    }
1381
1382    /// Unlocks a locked [`NetHsm`] backend.
1383    pub(crate) fn unlock_nethsm(&self) -> Result<(), crate::Error> {
1384        Ok(self
1385            .nethsm
1386            .unlock(self.admin_credentials.unlock_passphrase().clone())?)
1387    }
1388
1389    /// Retrieves the state for all users on the [`NetHsm`] backend.
1390    ///
1391    /// # Note
1392    ///
1393    /// Uses the `nethsm` with the [default
1394    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`].
1395    ///
1396    /// # Errors
1397    ///
1398    /// Returns an error if
1399    ///
1400    /// - using the credentials of the default *R-Administrator* fails,
1401    /// - retrieving all user names of the NetHSM backend fails,
1402    /// - retrieving information about a specific NetHSM user fails,
1403    /// - or retrieving the tags of an *Operator* user fails.
1404    pub(crate) fn user_states(&self) -> Result<Vec<UserState>, crate::Error> {
1405        info!(
1406            "Retrieve all user states from NetHSM backend at \"{}\"",
1407            self.nethsm.get_url()
1408        );
1409        // Use the default R-Administrator.
1410        self.nethsm
1411            .use_credentials(&self.admin_credentials.default_administrator()?.name)?;
1412
1413        let users = {
1414            let mut users: Vec<UserState> = Vec::new();
1415
1416            for user_id in self.nethsm.get_users()? {
1417                let user_data = self.nethsm.get_user(&user_id)?;
1418                let tag = {
1419                    // Only Operator users can have tags assigned to them.
1420                    if user_data.role == UserRole::Operator.into() {
1421                        let user_tags = self.nethsm.get_user_tags(&user_id)?;
1422                        match user_tags.len() {
1423                            0 => None,
1424                            1 => user_tags.first().cloned(),
1425                            number => {
1426                                return Err(
1427                                    Error::UserUnexpectedNumberOfTags { user_id, number }.into()
1428                                );
1429                            }
1430                        }
1431                    } else {
1432                        None
1433                    }
1434                };
1435
1436                users.push(UserState {
1437                    name: user_id,
1438                    role: user_data.role.into(),
1439                    tag,
1440                });
1441            }
1442
1443            users
1444        };
1445
1446        Ok(users)
1447    }
1448
1449    /// Retrieves the state of a key certificate on the [`NetHsm`] backend.
1450    ///
1451    /// Key certificates may be retrieved for system-wide keys or namespaced keys.
1452    /// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1453    /// retrieved.
1454    ///
1455    /// # Note
1456    ///
1457    /// It is assumed that the current credentials for the `nethsm` provide access to the key
1458    /// certificate of key `key_id`.
1459    fn key_certificate_state(
1460        &self,
1461        key_id: &KeyId,
1462        namespace: Option<&NamespaceId>,
1463    ) -> KeyCertificateState {
1464        // Provide a dedicated string for log messages in case a namespace is used.
1465        let namespace = if let Some(namespace) = namespace {
1466            format!(" in namespace \"{namespace}\"")
1467        } else {
1468            "".to_string()
1469        };
1470        info!(
1471            "Retrieve the key certificate state for key {key_id}{namespace} from NetHSM backend at \"{}\"",
1472            self.nethsm.get_url()
1473        );
1474
1475        match self.nethsm.get_key_certificate(key_id) {
1476            Ok(Some(key_cert)) => {
1477                let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1478                    Ok((public_key, _armor_header)) => public_key,
1479                    Err(error) => {
1480                        let message = format!(
1481                            "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1482                        );
1483                        debug!("{message}");
1484                        return KeyCertificateState::NotAnOpenPgpCertificate { message };
1485                    }
1486                };
1487
1488                match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1489                    Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1490                    Err(error) => {
1491                        let message = format!(
1492                            "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1493                        );
1494                        debug!("{message}");
1495                        KeyCertificateState::NotACryptographicKeyContext { message }
1496                    }
1497                }
1498            }
1499            Ok(None) => KeyCertificateState::Empty,
1500            Err(error) => {
1501                let message = error.to_string();
1502                debug!("{message}");
1503                KeyCertificateState::Error { message }
1504            }
1505        }
1506    }
1507
1508    /// Retrieves the state for all keys on the [`NetHsm`] backend.
1509    ///
1510    /// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1511    /// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1512    ///
1513    /// # Note
1514    ///
1515    /// This function uses the `nethsm` with the [default
1516    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`], but may switch to a
1517    /// namespace-specific _N-Administrator_ for individual operations.
1518    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1519    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`] again.
1520    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1521    ///
1522    ///
1523    /// # Errors
1524    ///
1525    /// Returns an error if
1526    ///
1527    /// - using the default *R-Administrator* for authentication against the backend fails,
1528    /// - retrieving the names of all system-wide keys on the backend fails,
1529    /// - retrieving information on a specific system-wide key on the backend fails,
1530    /// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1531    /// - using the credentials of an *N-Administrator* fails,
1532    /// - retrieving the names of all namespaced keys on the backend fails,
1533    /// - or retrieving information on a specific namespaced key on the backend fails.
1534    pub(crate) fn key_states(&self) -> Result<Vec<KeyState>, crate::Error> {
1535        info!(
1536            "Retrieve all key states from NetHSM backend at \"{}\"",
1537            self.nethsm.get_url()
1538        );
1539        // Use the default administrator
1540        let default_admin = &self.admin_credentials.default_administrator()?.name;
1541        self.nethsm.use_credentials(default_admin)?;
1542
1543        let mut keys = Vec::new();
1544        // Get the state of system-wide keys.
1545        for key_id in self.nethsm.get_keys(None)? {
1546            let key = self.nethsm.get_key(&key_id)?;
1547            let key_context = self.key_certificate_state(&key_id, None);
1548            let tag = {
1549                let tags = key.restrictions.tags.unwrap_or_default();
1550                if tags.len() > 1 {
1551                    return Err(Error::KeyUnexpectedNumberOfTags {
1552                        key_id,
1553                        number: tags.len(),
1554                    }
1555                    .into());
1556                }
1557
1558                if let Some(tag) = tags.first() {
1559                    tag.clone()
1560                } else {
1561                    return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1562                }
1563            };
1564
1565            keys.push(KeyState {
1566                name: key_id,
1567                namespace: None,
1568                tag,
1569                key_type: key
1570                    .r#type
1571                    .try_into()
1572                    .map_err(nethsm::Error::SignstarCrypto)?,
1573                mechanisms: key
1574                    .mechanisms
1575                    .iter()
1576                    .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1577                    .collect(),
1578                key_cert_state: key_context,
1579            });
1580        }
1581
1582        let mut seen_namespaces = HashSet::new();
1583        // Get the state of namespaced keys.
1584        for user_id in self
1585            .admin_credentials
1586            .namespace_administrators()
1587            .iter()
1588            .map(|creds| creds.name.clone())
1589        {
1590            // Extract the namespace of the user and ensure that the namespace exists already.
1591            let Some(namespace) = user_id.namespace() else {
1592                return Err(Error::NamespaceUserNoNamespace {
1593                    user: user_id.clone(),
1594                }
1595                .into());
1596            };
1597
1598            // Only extract key information for the namespace if we have not already looked at it.
1599            if seen_namespaces.contains(namespace) {
1600                continue;
1601            }
1602            seen_namespaces.insert(namespace.clone());
1603
1604            self.nethsm.use_credentials(&user_id)?;
1605            for key_id in self.nethsm.get_keys(None)? {
1606                let key = self.nethsm.get_key(&key_id)?;
1607                let key_context = self.key_certificate_state(&key_id, Some(namespace));
1608                let tag = {
1609                    let tags = key.restrictions.tags.unwrap_or_default();
1610                    if tags.len() > 1 {
1611                        return Err(Error::KeyUnexpectedNumberOfTags {
1612                            key_id,
1613                            number: tags.len(),
1614                        }
1615                        .into());
1616                    }
1617
1618                    if let Some(tag) = tags.first() {
1619                        tag.clone()
1620                    } else {
1621                        return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1622                    }
1623                };
1624
1625                keys.push(KeyState {
1626                    name: key_id,
1627                    namespace: Some(namespace.clone()),
1628                    tag,
1629                    key_type: key
1630                        .r#type
1631                        .try_into()
1632                        .map_err(nethsm::Error::SignstarCrypto)?,
1633                    mechanisms: key
1634                        .mechanisms
1635                        .iter()
1636                        .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1637                        .collect(),
1638                    key_cert_state: key_context,
1639                });
1640            }
1641        }
1642
1643        // Always use the default *R-Administrator* again.
1644        self.nethsm.use_credentials(default_admin)?;
1645
1646        Ok(keys)
1647    }
1648
1649    /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1650    /// non-administrative roles.
1651    ///
1652    /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1653    /// Then works down the following list to
1654    ///
1655    /// - create _R-Administrators_,
1656    ///     - or set their passphrase if they exist already,
1657    /// - create system-wide keys and add tags to them,
1658    ///     - or remove all tags from existing keys and only add the configured tags,
1659    /// - create users in the system-wide, non-administrative roles (i.e.
1660    ///   [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1661    ///   [`Operator`][`UserRole::Operator`]),
1662    ///     - or set their passphrase if they exist already,
1663    /// - create OpenPGP certificates for system-wide keys,
1664    ///     - or do nothing if they exist already,
1665    /// - create _N-Administrators_ and their respective namespaces,
1666    ///     - or set their passphrase if they exist already,
1667    /// - create namespaced keys and add tags to them,
1668    ///     - or remove all tags from existing keys and only add the configured tags,
1669    /// - create users in the namespaced, non-administrative roles (i.e.
1670    ///   [`Operator`][`UserRole::Operator`]),
1671    ///     - or set their passphrase if they exist already,
1672    /// - and create OpenPGP certificates for namespaced keys,
1673    ///     - or do nothing if they exist already.
1674    ///
1675    /// # Note
1676    ///
1677    /// This function uses the `nethsm` with the [default
1678    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`], but may switch to a
1679    /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1680    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1681    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`] again.
1682    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1683    /// non-administrative user.
1684    ///
1685    /// # Errors
1686    ///
1687    /// Returns an error if
1688    ///
1689    /// - retrieving the state of the [`NetHsm`] backend fails,
1690    /// - provisioning an unprovisioned [`NetHsm`] fails,
1691    /// - unlocking a locked [`NetHsm`] backend fails,
1692    /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1693    /// - adding system-wide keys fails,
1694    /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1695    ///   [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1696    /// - adding OpenPGP certificates for system-wide keys fails,
1697    /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1698    ///   their respective namespace fails,
1699    /// - adding namespaced keys fails,
1700    /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1701    /// - or adding OpenPGP certificates for namespaced keys fails.
1702    pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1703        debug!(
1704            "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1705            self.nethsm.get_url()
1706        );
1707
1708        // Extract user mappings for non-administrative users.
1709        let non_admin_users = self
1710            .nethsm_config
1711            .mappings()
1712            .iter()
1713            .filter(|mapping| !matches!(mapping, NetHsmUserMapping::Admin(..)))
1714            .collect::<Vec<_>>();
1715
1716        match self.nethsm.state()? {
1717            SystemState::Unprovisioned => {
1718                debug!(
1719                    "Unprovisioned NetHSM backend detected at {}",
1720                    self.nethsm.get_url()
1721                );
1722
1723                self.nethsm.provision(
1724                    self.admin_credentials.unlock_passphrase().clone(),
1725                    self.admin_credentials
1726                        .default_administrator()?
1727                        .passphrase
1728                        .clone(),
1729                    nethsm::Utc::now(),
1730                )?;
1731            }
1732            SystemState::Locked => {
1733                debug!(
1734                    "Locked NetHSM backend detected at {}",
1735                    self.nethsm.get_url()
1736                );
1737
1738                self.nethsm
1739                    .unlock(self.admin_credentials.unlock_passphrase().clone())?;
1740            }
1741            SystemState::Operational => {
1742                debug!(
1743                    "Operational NetHSM backend detected at {}",
1744                    self.nethsm.get_url()
1745                );
1746            }
1747        }
1748
1749        // Add any missing users and keys.
1750        add_system_wide_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1751        add_system_wide_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1752        add_non_administrative_users(
1753            &self.nethsm,
1754            self.admin_credentials,
1755            &non_admin_users,
1756            user_credentials,
1757        )?;
1758        add_system_wide_openpgp_certificates(
1759            &self.nethsm,
1760            self.admin_credentials,
1761            &non_admin_users,
1762        )?;
1763        add_namespace_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1764        add_namespaced_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1765        add_namespaced_non_administrative_users(
1766            &self.nethsm,
1767            self.admin_credentials,
1768            &non_admin_users,
1769            user_credentials,
1770        )?;
1771        add_namespaced_openpgp_certificates(
1772            &self.nethsm,
1773            self.admin_credentials,
1774            &non_admin_users,
1775        )?;
1776
1777        Ok(())
1778    }
1779}
1780
1781/// The state of a user in a [`NetHsmBackend`].
1782#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1783pub(crate) struct UserState {
1784    /// The name of the user.
1785    pub name: UserId,
1786    /// The role of the user.
1787    pub role: UserRole,
1788    /// The optional tag assigned to the user.
1789    pub tag: Option<String>,
1790}
1791
1792impl Display for UserState {
1793    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1794        write!(f, "{} (role: {}", self.name, self.role)?;
1795        if let Some(tag) = self.tag.as_ref() {
1796            write!(f, "; tag: {tag}")?;
1797        }
1798        write!(f, ")")?;
1799
1800        Ok(())
1801    }
1802}
1803
1804/// The state of a key in a [`NetHsmBackend`].
1805#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1806pub(crate) struct KeyState {
1807    /// The name of the key.
1808    pub name: KeyId,
1809    /// The optional namespace the key is used in.
1810    pub namespace: Option<NamespaceId>,
1811    /// The tag assigned to the key.
1812    pub tag: String,
1813    /// The key type of the key.
1814    pub key_type: KeyType,
1815    /// The mechanisms supported by the key.
1816    pub mechanisms: Vec<KeyMechanism>,
1817    /// The context in which the key is used.
1818    pub key_cert_state: KeyCertificateState,
1819}
1820
1821impl Display for KeyState {
1822    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1823        write!(f, "{} (", self.name)?;
1824        if let Some(namespace) = self.namespace.as_ref() {
1825            write!(f, "namespace: {namespace}; ")?;
1826        }
1827        write!(f, "tag: {}; ", self.tag)?;
1828        write!(f, "type: {}; ", self.key_type)?;
1829        write!(
1830            f,
1831            "mechanisms: {}; ",
1832            self.mechanisms
1833                .iter()
1834                .map(|mechanism| mechanism.to_string())
1835                .collect::<Vec<String>>()
1836                .join(", ")
1837        )?;
1838        write!(f, "context: {}", self.key_cert_state)?;
1839        write!(f, ")")?;
1840
1841        Ok(())
1842    }
1843}
1844
1845/// The state of a [`NetHsmBackend`].
1846///
1847/// This tracks the available backend users, their roles and assigned tags, and the key setups
1848/// associated with users.
1849#[derive(Debug, Eq, PartialEq)]
1850pub struct NetHsmBackendState {
1851    /// The user states.
1852    pub(crate) user_states: Vec<UserState>,
1853    /// The key states.
1854    pub(crate) key_states: Vec<KeyState>,
1855}
1856
1857impl NetHsmBackendState {
1858    /// The name of the origin for the state.
1859    pub const STATE_NAME: &'static str = "NetHSM backend";
1860}
1861
1862impl StateOriginInfo for NetHsmBackendState {
1863    fn state_name(&self) -> &str {
1864        Self::STATE_NAME
1865    }
1866
1867    fn state_origin(&self) -> StateOrigin {
1868        StateOrigin::Backend
1869    }
1870}
1871
1872impl<'a, 'b> TryFrom<&NetHsmBackend<'a, 'b>> for NetHsmBackendState {
1873    type Error = crate::Error;
1874
1875    /// Creates a new [`NetHsmBackendState`] from a [`NetHsmBackend`].
1876    ///
1877    /// # Note
1878    ///
1879    /// Uses the [`NetHsm`] backend with the [default
1880    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`], but may switch to a
1881    /// namespace-specific _N-Administrator_ for individual operations.
1882    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1883    /// _R-Administrator_][`NetHsmAdminCredentials::default_administrator`] again.
1884    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1885    ///
1886    /// # Errors
1887    ///
1888    /// Returns an error if
1889    ///
1890    /// - retrieving the system state of the [`NetHsm`] backend fails,
1891    /// - unlocking a locked [`NetHsm`] backend fails,
1892    /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
1893    fn try_from(value: &NetHsmBackend<'a, 'b>) -> Result<Self, Self::Error> {
1894        debug!(
1895            "Retrieve state of the NetHSM backend at {}",
1896            value.nethsm().get_url()
1897        );
1898
1899        let (user_states, key_states) = match value.nethsm().state()? {
1900            SystemState::Unprovisioned => {
1901                debug!(
1902                    "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
1903                    value.nethsm().get_url()
1904                );
1905
1906                (Vec::new(), Vec::new())
1907            }
1908            SystemState::Locked => {
1909                debug!(
1910                    "Locked NetHSM backend detected at {}",
1911                    value.nethsm().get_url()
1912                );
1913
1914                value.unlock_nethsm()?;
1915
1916                let user_states = value.user_states()?;
1917                let key_states = value.key_states()?;
1918
1919                (user_states, key_states)
1920            }
1921            SystemState::Operational => {
1922                debug!(
1923                    "Operational NetHSM backend detected at {}",
1924                    value.nethsm().get_url()
1925                );
1926
1927                let user_states = value.user_states()?;
1928                let key_states = value.key_states()?;
1929
1930                (user_states, key_states)
1931            }
1932        };
1933
1934        Ok(Self {
1935            user_states,
1936            key_states,
1937        })
1938    }
1939}
1940
1941#[cfg(test)]
1942#[cfg(feature = "_test-helpers")]
1943mod tests {
1944    use log::LevelFilter;
1945    use nethsm::{
1946        Connection,
1947        ConnectionSecurity,
1948        CryptographicKeyContext,
1949        FullCredentials,
1950        NetHsm,
1951        OpenPgpUserIdList,
1952        OpenPgpVersion,
1953        UserRole,
1954    };
1955    use rstest::rstest;
1956    use signstar_common::logging::setup_logging;
1957    use testresult::TestResult;
1958
1959    use super::*;
1960    use crate::test::{ConfigFileConfig, ConfigFileVariant, SystemPrepareConfig};
1961
1962    /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1963    /// [`NetHsmAdminCredentials`] and [`Config`].
1964    #[test]
1965    fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1966        setup_logging(LevelFilter::Debug)?;
1967
1968        let prepare_config = SystemPrepareConfig {
1969            machine_id: false,
1970            credentials_socket: false,
1971            signstar_config: ConfigFileConfig {
1972                location: None,
1973                variant: ConfigFileVariant::OnlyNetHsmBackendAdminPlaintextNonAdminSystemdCreds,
1974                system_user_config: None,
1975            },
1976        };
1977        let signstar_config = prepare_config.signstar_config.variant.to_config()?;
1978
1979        let nethsm = NetHsm::new(
1980            Connection::new(
1981                "https://example.org/api/v1".try_into()?,
1982                ConnectionSecurity::Unsafe,
1983            ),
1984            None,
1985            None,
1986            None,
1987        )?;
1988        // The administrative credentials.
1989        let admin_credentials = NetHsmAdminCredentials::new(
1990            // this is different from the one in the Signstar config.
1991            2,
1992            "backup-passphrase-really-just-for-testing-i-promise".parse()?,
1993            "unlock-passphrase-really-just-for-testing-i-promise".parse()?,
1994            vec![FullCredentials::new(
1995                "admin".parse()?,
1996                "admin-passphrase-really-just-for-testing-i-promise".parse()?,
1997            )],
1998            vec![FullCredentials::new(
1999                "ns1~admin".parse()?,
2000                "ns1-admin-passphrase-really-just-for-testing-i-promise".parse()?,
2001            )],
2002        )?;
2003        let nethsm_backend_result =
2004            NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
2005
2006        assert!(
2007            nethsm_backend_result.is_err(),
2008            "Test should have failed, but succeeded"
2009        );
2010        assert!(
2011            matches!(
2012                nethsm_backend_result,
2013                Err(crate::Error::IterationMismatch {
2014                    admin_creds: _,
2015                    signstar_config: _
2016                })
2017            ),
2018            "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
2019        );
2020
2021        Ok(())
2022    }
2023
2024    /// Ensures that [`UserState::to_string`] shows correctly.
2025    #[rstest]
2026    #[case(
2027        UserState{
2028            name: "testuser".parse()?,
2029            role: UserRole::Operator,
2030            tag: Some("tag1".to_string())
2031        },
2032        "testuser (role: Operator; tag: tag1)",
2033    )]
2034    #[case(
2035        UserState{
2036            name: "testuser".parse()?,
2037            role: UserRole::Operator,
2038            tag: None,
2039        },
2040        "testuser (role: Operator)",
2041    )]
2042    #[case(
2043        UserState{
2044            name: "testuser".parse()?,
2045            role: UserRole::Metrics,
2046            tag: None,
2047        },
2048        "testuser (role: Metrics)",
2049    )]
2050    #[case(
2051        UserState{
2052            name: "testuser".parse()?,
2053            role: UserRole::Backup,
2054            tag: None,
2055        },
2056        "testuser (role: Backup)",
2057    )]
2058    #[case(
2059        UserState{name:
2060            "testuser".parse()?,
2061            role: UserRole::Administrator,
2062            tag: None,
2063        },
2064        "testuser (role: Administrator)",
2065    )]
2066    fn user_state_to_string(#[case] user_state: UserState, #[case] expected: &str) -> TestResult {
2067        setup_logging(LevelFilter::Debug)?;
2068
2069        assert_eq!(user_state.to_string(), expected);
2070        Ok(())
2071    }
2072
2073    /// Ensures that [`KeyState::to_string`] shows correctly.
2074    #[rstest]
2075    #[case::namespaced_key_with_openpgp_v4_cert(
2076        KeyState{
2077            name: "key1".parse()?,
2078            namespace: Some("ns1".parse()?),
2079            tag: "tag1".to_string(),
2080            key_type: KeyType::Curve25519,
2081            mechanisms: vec![KeyMechanism::EdDsaSignature],
2082            key_cert_state: KeyCertificateState::KeyContext(
2083                CryptographicKeyContext::OpenPgp {
2084                    user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
2085                    version: OpenPgpVersion::V4,
2086                })
2087        },
2088        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: OpenPGP (Version: 4; User IDs: \"John Doe <john@example.org>\"))",
2089    )]
2090    #[case::namespaced_key_with_raw_cert(
2091        KeyState{
2092            name: "key1".parse()?,
2093            namespace: Some("ns1".parse()?),
2094            tag: "tag1".to_string(),
2095            key_type: KeyType::Curve25519,
2096            mechanisms: vec![KeyMechanism::EdDsaSignature],
2097            key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2098        },
2099        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2100    )]
2101    #[case::namespaced_key_with_no_cert(
2102        KeyState{
2103            name: "key1".parse()?,
2104            namespace: Some("ns1".parse()?),
2105            tag: "tag1".to_string(),
2106            key_type: KeyType::Curve25519,
2107            mechanisms: vec![KeyMechanism::EdDsaSignature],
2108            key_cert_state: KeyCertificateState::Empty
2109        },
2110        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Empty)",
2111    )]
2112    #[case::namespaced_key_with_cert_error(
2113        KeyState{
2114            name: "key1".parse()?,
2115            namespace: Some("ns1".parse()?),
2116            tag: "tag1".to_string(),
2117            key_type: KeyType::Curve25519,
2118            mechanisms: vec![KeyMechanism::EdDsaSignature],
2119            key_cert_state: KeyCertificateState::Error { message: "the dog ate it".to_string() }
2120        },
2121        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Error retrieving key certificate - the dog ate it)",
2122    )]
2123    #[case::namespaced_key_with_not_a_cert_context(
2124        KeyState{
2125            name: "key1".parse()?,
2126            namespace: Some("ns1".parse()?),
2127            tag: "tag1".to_string(),
2128            key_type: KeyType::Curve25519,
2129            mechanisms: vec![KeyMechanism::EdDsaSignature],
2130            key_cert_state: KeyCertificateState::NotACryptographicKeyContext { message: "failed to convert".to_string() }
2131        },
2132        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not a cryptographic key context - \"failed to convert\")",
2133    )]
2134    #[case::namespaced_key_with_not_an_openpgp_cert(
2135        KeyState{
2136            name: "key1".parse()?,
2137            namespace: Some("ns1".parse()?),
2138            tag: "tag1".to_string(),
2139            key_type: KeyType::Curve25519,
2140            mechanisms: vec![KeyMechanism::EdDsaSignature],
2141            key_cert_state: KeyCertificateState::NotAnOpenPgpCertificate { message: "it's a blob".to_string() }
2142        },
2143        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not an OpenPGP certificate - \"it's a blob\")",
2144    )]
2145    #[case::system_wide_key_with_no_cert_and_no_tags_and_raw_cert(
2146        KeyState{
2147            name: "key1".parse()?,
2148            namespace: None,
2149            tag: "tag1".to_string(),
2150            key_type: KeyType::Curve25519,
2151            mechanisms: vec![KeyMechanism::EdDsaSignature],
2152            key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2153        },
2154        "key1 (tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2155    )]
2156    fn key_state_to_string(#[case] key_state: KeyState, #[case] expected: &str) -> TestResult {
2157        setup_logging(LevelFilter::Debug)?;
2158
2159        assert_eq!(key_state.to_string(), expected);
2160        Ok(())
2161    }
2162}