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