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::SignstarCryptoKey)?,
657                    key_mechanisms: info
658                        .mechanisms
659                        .iter()
660                        .filter_map(|mechanism| mechanism.try_into().ok())
661                        .collect(),
662                },
663            );
664
665            // Remove all existing tags.
666            if let Some(available_tags) = info.restrictions.tags {
667                for available_tag in available_tags {
668                    nethsm.delete_key_tag(user_key_data.key_id, available_tag.as_str())?;
669                }
670            }
671            // Add the required tag to the key.
672            nethsm.add_key_tag(user_key_data.key_id, user_key_data.tag)?;
673        } else {
674            // Add the key, including the required tag.
675            nethsm.generate_key(
676                user_key_data.key_setup.key_type(),
677                user_key_data.key_setup.key_mechanisms().to_vec(),
678                user_key_data.key_setup.key_length(),
679                Some(user_key_data.key_id.clone()),
680                Some(vec![user_key_data.tag.to_string()]),
681            )?;
682        }
683    }
684
685    Ok(())
686}
687
688/// Sets up all namespaced keys and tags them.
689///
690/// Creates any missing keys and adds the configured tags for all of them.
691/// If keys exist already, deletes all tags and adds the configured ones for them.
692///
693/// # Note
694///
695/// It is assumed that _N-Administrators_ have already been set up, before calling
696/// this function (see `add_namespace_admins`).
697///
698/// This function uses the `nethsm` with the [default
699/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
700/// namespace-specific _N-Administrator_ for individual operations.
701/// If this function succeeds, the `nethsm` is guaranteed to use the [default
702/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
703/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
704///
705/// This function does not fail on mismatching keys, as it is assumed that keys are added
706/// intentionally and should not be deleted/altered.
707/// However, warnings are emitted if an existing key has a mismatching key type or key mechanisms
708/// from what is configured in the Signstar configuration file.
709///
710/// Opposite to the behavior of `add_system_wide_keys`, this function does not delete any tags from
711/// keys.
712/// This is due to [a bug in the NetHSM firmware], which leads to a crash when adding a tag to a
713/// key, trying to remove and then re-adding it again.
714///
715/// # Errors
716///
717/// Returns an error if
718///
719/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
720/// - retrieving the list of available users from the NetHSM backend fails,
721/// - a namespaced user mapped to a key is not in a namespace,
722/// - no usable *N-Administrator* for a namespace is known,
723/// - the available keys in the namespace cannot be retrieved,
724/// - information about a specific key in the namespace cannot be retrieved,
725/// - a tag cannot be added to an already existing key,
726/// - a new key cannot be generated,
727/// - or using the default system-wide administrator again fails.
728///
729/// [a bug in the NetHSM firmware]: https://github.com/Nitrokey/nethsm/issues/13
730fn add_namespaced_keys(
731    nethsm: &NetHsm,
732    admin_credentials: &NetHsmAdminCredentials,
733    user_mappings: &[&NetHsmUserMapping],
734) -> Result<(), crate::Error> {
735    debug!(
736        "Setup namespaced cryptographic keys on NetHSM backend at {}",
737        nethsm.get_url()
738    );
739
740    // Use the default R-Administrator for authentication to the backend by default.
741    let default_admin = &admin_credentials.get_default_administrator()?.name;
742    nethsm.use_credentials(default_admin)?;
743
744    let available_users = nethsm.get_users()?;
745
746    let all_user_key_data = user_mappings
747        .iter()
748        .filter_map(|user_mapping| {
749            user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::Namespaced)
750        })
751        .collect::<Vec<_>>();
752
753    for user_key_data in all_user_key_data {
754        debug!(
755            "Set up key \"{}\" with tag {} for user {}",
756            user_key_data.key_id, user_key_data.tag, user_key_data.user
757        );
758
759        // Extract the namespace from the user or return an error.
760        let Some(namespace) = user_key_data.user.namespace() else {
761            // Note: Returning this error is not really possible, as we are explicitly
762            // requesting tuples of namespaced user, key setup and tag.
763            return Err(Error::NamespaceUserNoNamespace {
764                user: user_key_data.user.clone(),
765            }
766            .into());
767        };
768
769        // Select the first available N-Administrator credentials for interacting with the
770        // NetHSM backend.
771        nethsm.use_credentials(&get_first_available_namespace_admin(
772            nethsm,
773            admin_credentials,
774            &available_users,
775            namespace,
776        )?)?;
777
778        let available_keys = nethsm.get_keys(None)?;
779
780        if available_keys.contains(user_key_data.key_id) {
781            let key_info = nethsm.get_key(user_key_data.key_id)?;
782
783            // Compare the key setups.
784            compare_key_setups(
785                user_key_data.key_id,
786                Some(namespace),
787                KeySetupComparison {
788                    state_origin: StateOrigin::Config,
789                    key_type: user_key_data.key_setup.key_type(),
790                    key_mechanisms: HashSet::from_iter(
791                        user_key_data.key_setup.key_mechanisms().to_vec(),
792                    ),
793                },
794                KeySetupComparison {
795                    state_origin: StateOrigin::Backend,
796                    key_type: key_info
797                        .r#type
798                        .try_into()
799                        .map_err(nethsm::Error::SignstarCryptoKey)?,
800                    key_mechanisms: key_info
801                        .mechanisms
802                        .iter()
803                        .filter_map(|mechanism| mechanism.try_into().ok())
804                        .collect(),
805                },
806            );
807
808            // If there are tags already, check if the tag we are looking for is already set and
809            // if so, skip to the next key.
810            if let Some(available_tags) = key_info.restrictions.tags {
811                debug!(
812                    "Available tags for key \"{}\" in namespace {namespace}: {}",
813                    user_key_data.key_id,
814                    available_tags.join(", ")
815                );
816                // NOTE: If the required tag is already set, continue to the next key.
817                //       Without this we otherwise trigger a bug in the NetHSM firmware which
818                //       breaks the connection after re-adding the tag for the key further down.
819                //       (i.e. "Bad Status: HTTP version did not start with HTTP/")
820                //       See https://github.com/Nitrokey/nethsm/issues/13 for details.
821                if available_tags.len() == 1
822                    && available_tags
823                        .iter()
824                        .find(|tag| tag.as_str() == user_key_data.tag)
825                        .is_some()
826                {
827                    continue;
828                }
829            }
830
831            // Add the tag to the key.
832            nethsm.add_key_tag(user_key_data.key_id, user_key_data.tag)?;
833        } else {
834            // Add the key, including the required tag.
835            nethsm.generate_key(
836                user_key_data.key_setup.key_type(),
837                user_key_data.key_setup.key_mechanisms().to_vec(),
838                user_key_data.key_setup.key_length(),
839                Some(user_key_data.key_id.clone()),
840                Some(vec![user_key_data.tag.to_string()]),
841            )?;
842        }
843    }
844
845    // Always use the default R-Administrator again.
846    nethsm.use_credentials(default_admin)?;
847
848    Ok(())
849}
850
851/// Adds OpenPGP certificates for system-wide keys that are used for OpenPGP signing.
852///
853/// # Note
854///
855/// It is assumed that the [default
856/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all system-wide keys
857/// and all system-wide non-administrative users are already set up, before calling this function
858/// (see `add_system_wide_admins`, `add_system_wide_keys` and `add_non_administrative_users`,
859/// respectively).
860///
861/// This function uses the `nethsm` with the [default
862/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
863/// system-wide, non-administrative user for individual operations.
864/// If this function succeeds, the `nethsm` is guaranteed to use the [default
865/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
866/// If this function fails, the `nethsm` may still use a system-wide, non-administrative user.
867///
868/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
869/// inconsistencies between signatures created with a previous version of a certificate and those
870/// created with a new version of the certificate, which is hard to debug.
871///
872/// # Errors
873///
874/// Returns an error if
875///
876/// - using the default *R-Administrator* fails,
877/// - retrieving the names of all system-wide users fails,
878/// - retrieving the names of all system-wide keys fails,
879/// - a user used for OpenPGP signing does not exist,
880/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
881/// - a user used for OpenPGP signing does not have a required tag,
882/// - a key used for OpenPGP signing does not exist,
883/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
884/// - a key used for OpenPGP signing does not have a required tag,
885/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
886/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
887///   create an OpenPGP certificate for the key,
888/// - or the default *R-Administrator* cannot be used to import the generated OpenPGP certificate
889///   for the key.
890fn add_system_wide_openpgp_certificates(
891    nethsm: &NetHsm,
892    admin_credentials: &NetHsmAdminCredentials,
893    user_mappings: &[&NetHsmUserMapping],
894) -> Result<(), crate::Error> {
895    debug!(
896        "Setup OpenPGP certificates for system-wide cryptographic keys on NetHSM backend at {}",
897        nethsm.get_url()
898    );
899
900    // Use the default R-Administrator for authentication to the backend by default.
901    let default_admin = &admin_credentials.get_default_administrator()?.name;
902    nethsm.use_credentials(default_admin)?;
903
904    let available_users = nethsm.get_users()?;
905
906    let all_user_key_data = user_mappings
907        .iter()
908        .filter_map(|user_mapping| {
909            user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::SystemWide)
910        })
911        .collect::<Vec<_>>();
912
913    for user_key_data in all_user_key_data {
914        // Get OpenPGP User IDs and version or continue to the next user/key setup if the
915        // mapping is not used for OpenPGP signing.
916        let CryptographicKeyContext::OpenPgp { user_ids, version } =
917            user_key_data.key_setup.key_context()
918        else {
919            debug!(
920                "Skip creating an OpenPGP certificate for the key \"{}\" used by user \"{}\" as it is not used in an OpenPGP context.",
921                user_key_data.key_id, user_key_data.user,
922            );
923            continue;
924        };
925
926        // Ensure the targeted user exists.
927        if !available_users.contains(user_key_data.user) {
928            return Err(Error::UserMissing {
929                user_id: user_key_data.user.clone(),
930            }
931            .into());
932        }
933        // Ensure the required tag is assigned to the targeted user.
934        if nethsm
935            .get_user_tags(user_key_data.user)?
936            .iter()
937            .find(|tag| tag.as_str() == user_key_data.tag)
938            .is_none()
939        {
940            return Err(Error::UserMissingTag {
941                user_id: user_key_data.user.clone(),
942                tag: user_key_data.tag.to_string(),
943            }
944            .into());
945        }
946
947        let available_keys = nethsm.get_keys(None)?;
948
949        // Ensure the targeted key exists.
950        if !available_keys.contains(user_key_data.key_id) {
951            return Err(Error::KeyMissing {
952                key_id: user_key_data.key_id.clone(),
953            }
954            .into());
955        }
956        // Ensure the required tag is assigned to the targeted key.
957        if !nethsm
958            .get_key(user_key_data.key_id)?
959            .restrictions
960            .tags
961            .is_some_and(|tags| {
962                tags.iter()
963                    .find(|tag| tag.as_str() == user_key_data.tag)
964                    .is_some()
965            })
966        {
967            return Err(Error::KeyIsMissingTag {
968                key_id: user_key_data.key_id.clone(),
969                tag: user_key_data.tag.to_string(),
970            }
971            .into());
972        }
973
974        // Create the OpenPGP certificate if it does not exist yet.
975        if nethsm.get_key_certificate(user_key_data.key_id)?.is_none() {
976            // Ensure the first OpenPGP User ID exists.
977            let Some(user_id) = user_ids.first() else {
978                return Err(Error::OpenPgpUserIdMissing {
979                    key_id: user_key_data.key_id.clone(),
980                }
981                .into());
982            };
983
984            // Switch to the dedicated user with access to the key to create an OpenPGP
985            // certificate for the key.
986            nethsm.use_credentials(user_key_data.user)?;
987            let data = nethsm.create_openpgp_cert(
988                user_key_data.key_id,
989                OpenPgpKeyUsageFlags::default(),
990                user_id.clone(),
991                Timestamp::now(),
992                *version,
993            )?;
994
995            // Switch back to the default R-Administrator for the import of the OpenPGP
996            // certificate.
997            nethsm.use_credentials(default_admin)?;
998            nethsm.import_key_certificate(user_key_data.key_id, data)?;
999        }
1000    }
1001
1002    // Always use the default R-Administrator again.
1003    nethsm.use_credentials(default_admin)?;
1004
1005    Ok(())
1006}
1007
1008/// Adds OpenPGP certificates for namespaced keys that are used for OpenPGP signing.
1009///
1010/// # Note
1011///
1012/// It is assumed that the [default
1013/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all namespaced keys,
1014/// all _N-Administrators_ and all namespaced non-administrative users are already set up, before
1015/// calling this function (see `add_system_wide_admins`, `add_namespaced_keys`,
1016/// `add_namespace_admins` and `add_namespaced_non_administrative_users`, respectively).
1017///
1018/// This function uses the `nethsm` with the [default
1019/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1020/// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1021/// If this function succeeds, the `nethsm` is guaranteed to use the [default
1022/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1023/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1024/// non-administrative user.
1025///
1026/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
1027/// inconsistencies between signatures created with a previous version of a certificate and those
1028/// created with a new version of the certificate, which is hard to debug.
1029///
1030/// # Errors
1031///
1032/// Returns an error if
1033///
1034/// - using the default *R-Administrator* fails,
1035/// - retrieving the names of all users fails,
1036/// - a namespaced user is not in a namespace,
1037/// - no usable *N-Administrator* for a namespace is known,
1038/// - a user used for OpenPGP signing does not exist,
1039/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
1040/// - a user used for OpenPGP signing does not have a required tag,
1041/// - retrieving the names of all keys in a namespace fails,
1042/// - a key used for OpenPGP signing does not exist,
1043/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
1044/// - a key used for OpenPGP signing does not have a required tag,
1045/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
1046/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
1047///   create an OpenPGP certificate for the key,
1048/// - or the *N-Administrator* cannot be used to import the generated OpenPGP certificate for the
1049///   key.
1050fn add_namespaced_openpgp_certificates(
1051    nethsm: &NetHsm,
1052    admin_credentials: &NetHsmAdminCredentials,
1053    user_mappings: &[&NetHsmUserMapping],
1054) -> Result<(), crate::Error> {
1055    debug!(
1056        "Setup OpenPGP certificates for namespaced cryptographic keys on NetHSM backend at {}",
1057        nethsm.get_url()
1058    );
1059
1060    // Use the default R-Administrator for authentication to the backend by default.
1061    let default_admin = &admin_credentials.get_default_administrator()?.name;
1062    nethsm.use_credentials(default_admin)?;
1063
1064    let available_users = nethsm.get_users()?;
1065
1066    let nethsm_user_key_data_list = user_mappings
1067        .iter()
1068        .filter_map(|user_mapping| {
1069            let Some(user_key_data) =
1070                user_mapping.nethsm_config_user_key_data(NetHsmUserKeysFilter::Namespaced)
1071            else {
1072                // We are only interested in mappings that define key data.
1073                return None;
1074            };
1075            // We are only interested in mappings that define OpenPGP key data.
1076            if !matches!(
1077                user_key_data.key_setup.key_context(),
1078                CryptographicKeyContext::OpenPgp { .. }
1079            ) {
1080                return None;
1081            }
1082
1083            Some(user_key_data)
1084        })
1085        .collect::<Vec<_>>();
1086
1087    for user_key_data in nethsm_user_key_data_list {
1088        // Get OpenPGP User IDs and version or continue to the next user/key setup if the
1089        // mapping is not used for OpenPGP signing.
1090        let CryptographicKeyContext::OpenPgp { user_ids, version } =
1091            user_key_data.key_setup.key_context()
1092        else {
1093            continue;
1094        };
1095
1096        // Extract the namespace from the user.
1097        let Some(namespace) = user_key_data.user.namespace() else {
1098            // Note: Returning this error is not really possible, as we are explicitly
1099            // requesting tuples of namespaced user, key setup and tag.
1100            return Err(Error::NamespaceUserNoNamespace {
1101                user: user_key_data.user.clone(),
1102            }
1103            .into());
1104        };
1105
1106        // Select the first available N-Administrator credentials for interacting with the
1107        // NetHSM backend.
1108        let admin = get_first_available_namespace_admin(
1109            nethsm,
1110            admin_credentials,
1111            &available_users,
1112            namespace,
1113        )?;
1114        nethsm.use_credentials(&admin)?;
1115
1116        // Ensure the targeted user exists.
1117        if !available_users.contains(user_key_data.user) {
1118            return Err(Error::NamespaceUserMissing {
1119                user: user_key_data.user.clone(),
1120                namespace: namespace.clone(),
1121            }
1122            .into());
1123        }
1124        // Ensure the required tag is assigned to the targeted user.
1125        let user_tags = nethsm.get_user_tags(user_key_data.user)?;
1126        if user_tags
1127            .iter()
1128            .find(|tag| tag.as_str() == user_key_data.tag)
1129            .is_none()
1130        {
1131            return Err(Error::NamespaceUserMissingTag {
1132                user: user_key_data.user.clone(),
1133                namespace: namespace.clone(),
1134                tag: user_key_data.tag.to_string(),
1135            }
1136            .into());
1137        }
1138
1139        let available_keys = nethsm.get_keys(None)?;
1140
1141        // Ensure the targeted key exists.
1142        if !available_keys.contains(user_key_data.key_id) {
1143            return Err(Error::NamespaceKeyMissing {
1144                key_id: user_key_data.key_id.clone(),
1145                namespace: namespace.clone(),
1146            }
1147            .into());
1148        }
1149        // Ensure the required tag is assigned to the targeted key.
1150        let pubkey = nethsm.get_key(user_key_data.key_id)?;
1151        if !pubkey.restrictions.tags.is_some_and(|tags| {
1152            tags.iter()
1153                .find(|tag| tag.as_str() == user_key_data.tag)
1154                .is_some()
1155        }) {
1156            return Err(Error::NamespaceKeyMissesTag {
1157                key_id: user_key_data.key_id.clone(),
1158                namespace: namespace.clone(),
1159                tag: user_key_data.tag.to_string(),
1160            }
1161            .into());
1162        }
1163
1164        // Create the OpenPGP certificate if it does not exist yet.
1165        if nethsm.get_key_certificate(user_key_data.key_id)?.is_none() {
1166            // Ensure the first OpenPGP User ID exists.
1167            let Some(user_id) = user_ids.first() else {
1168                return Err(Error::NamespaceOpenPgpUserIdMissing {
1169                    key_id: user_key_data.key_id.clone(),
1170                    namespace: namespace.clone(),
1171                }
1172                .into());
1173            };
1174
1175            // Switch to the dedicated user with access to the key to create an OpenPGP
1176            // certificate for the key.
1177            nethsm.use_credentials(user_key_data.user)?;
1178            let data = nethsm.create_openpgp_cert(
1179                user_key_data.key_id,
1180                OpenPgpKeyUsageFlags::default(),
1181                user_id.clone(),
1182                Timestamp::now(),
1183                *version,
1184            )?;
1185
1186            // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1187            nethsm.use_credentials(&admin)?;
1188            nethsm.import_key_certificate(user_key_data.key_id, data)?;
1189        }
1190    }
1191
1192    // Always use the default R-Administrator again.
1193    nethsm.use_credentials(default_admin)?;
1194
1195    Ok(())
1196}
1197
1198/// A NetHSM backend that provides full control over its data.
1199///
1200/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1201/// by the tracked [`NetHsmAdminCredentials`] and [`Config`].
1202#[derive(Debug)]
1203pub struct NetHsmBackend<'a, 'b> {
1204    nethsm: NetHsm,
1205    admin_credentials: &'a NetHsmAdminCredentials,
1206    nethsm_config: &'b NetHsmConfig,
1207}
1208
1209impl<'a, 'b> NetHsmBackend<'a, 'b> {
1210    /// Creates a new [`NetHsmBackend`].
1211    ///
1212    /// Returns `Some(None)` if `signstar_config` contains no [`NetHsmConfig`].
1213    ///
1214    /// # Errors
1215    ///
1216    /// Returns an error if
1217    ///
1218    /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1219    /// - or retrieving the default administrator from the `admin_credentials` fails.
1220    ///
1221    /// # Examples
1222    ///
1223    /// ```
1224    /// use std::{collections::BTreeSet, num::NonZeroUsize};
1225    ///
1226    /// use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1227    /// use signstar_config::{
1228    ///     config::{ConfigBuilder, SystemConfig, SystemUserMapping},
1229    ///     nethsm::{NetHsmAdminCredentials, NetHsmBackend, NetHsmConfig, NetHsmMetricsUsers, NetHsmUserMapping},
1230    /// };
1231    /// use signstar_crypto::{
1232    ///     AdministrativeSecretHandling,
1233    ///     NonAdministrativeSecretHandling,
1234    ///     key::{CryptographicKeyContext, KeyMechanism, KeyType, SigningKeySetup, SignatureType},
1235    ///     openpgp::OpenPgpUserIdList,
1236    /// };
1237    ///
1238    /// # fn main() -> testresult::TestResult {
1239    /// // The NetHSM connection.
1240    /// let nethsm = NetHsm::new(
1241    ///     Connection::new(
1242    ///         "https://example.org/api/v1".try_into()?,
1243    ///         ConnectionSecurity::Unsafe,
1244    ///     ),
1245    ///     None,
1246    ///     None,
1247    ///     None,
1248    /// )?;
1249    /// // The administrative credentials.
1250    /// let admin_credentials = NetHsmAdminCredentials::new(
1251    ///     1,
1252    ///     "backup-passphrase".parse()?,
1253    ///     "unlock-passphrase".parse()?,
1254    ///     vec![FullCredentials::new(
1255    ///         "admin".parse()?,
1256    ///         "admin-passphrase".parse()?,
1257    ///     )],
1258    ///     vec![FullCredentials::new(
1259    ///         "ns1~admin".parse()?,
1260    ///         "ns1-admin-passphrase".parse()?,
1261    ///     )],
1262    /// )?;
1263    /// // The Signstar config.
1264    /// let signstar_config = ConfigBuilder::new(SystemConfig::new(
1265    ///         1,
1266    ///         AdministrativeSecretHandling::ShamirsSecretSharing {
1267    ///             number_of_shares: NonZeroUsize::new(3).expect("3 is larger than 0"),
1268    ///             threshold: NonZeroUsize::new(2).expect("2 is larger than 0"),
1269    ///         },
1270    ///         NonAdministrativeSecretHandling::SystemdCreds,
1271    ///         BTreeSet::from_iter([
1272    ///             SystemUserMapping::ShareHolder {
1273    ///                 system_user: "share-holder1".parse()?,
1274    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAN54Gd1jMz+yNDjBRwX1SnOtWuUsVF64RJIeYJ8DI7b user@host".parse()?,
1275    ///             },
1276    ///             SystemUserMapping::ShareHolder {
1277    ///                 system_user: "share-holder2".parse()?,
1278    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDgwGfIRBAsOUuDEZw/uJQZSwOYr4sg2DAZpcc7MfOj user@host".parse()?,
1279    ///             },
1280    ///             SystemUserMapping::ShareHolder {
1281    ///                 system_user: "share-holder3".parse()?,
1282    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWqWyMCk5BdSl1c3KYoLEokKr7qNVPbI1IbBhgEBQj5 user@host".parse()?
1283    ///             },
1284    ///             SystemUserMapping::WireGuardDownload {
1285    ///                 system_user: "wireguard-downloader".parse()?,
1286    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh9BTe81DC6A0YZALsq9dWcyl6xjjqlxWPwlExTFgBt user@host".parse()?,
1287    ///             },
1288    ///         ]),
1289    ///     )?)
1290    ///     .set_nethsm_config(NetHsmConfig::new(
1291    ///         BTreeSet::from_iter([
1292    ///             Connection::new("https:///nethsm1.example.org/".parse()?, ConnectionSecurity::Unsafe),
1293    ///             Connection::new("https:///nethsm2.example.org/".parse()?, ConnectionSecurity::Unsafe),
1294    ///         ]),
1295    ///         BTreeSet::from_iter([
1296    ///             NetHsmUserMapping::Admin("admin".parse()?),
1297    ///             NetHsmUserMapping::Backup{
1298    ///                 backend_user: "backup".parse()?,
1299    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHxR0Oc+SWXkEvvZPitc6NvjvykgiKc9iauRI7tLYvcp user@host".parse()?,
1300    ///                 system_user: "nethsm-backup-user".parse()?,
1301    ///             },
1302    ///             NetHsmUserMapping::HermeticMetrics {
1303    ///                 backend_users: NetHsmMetricsUsers::new("hermeticmetrics".parse()?, vec!["hermetickeymetrics".parse()?])?,
1304    ///                 system_user: "nethsm-hermetic-metrics-user".parse()?,
1305    ///             },
1306    ///             NetHsmUserMapping::Metrics {
1307    ///                 backend_users: NetHsmMetricsUsers::new("metrics".parse()?, vec!["keymetrics".parse()?])?,
1308    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIETxhCqeZhfzFLfH0KFyw3u/w/dkRBUrft8tQm7DEVzY user@host".parse()?,
1309    ///                 system_user: "nethsm-metrics-user".parse()?,
1310    ///             },
1311    ///             NetHsmUserMapping::Signing {
1312    ///                 backend_user: "signing".parse()?,
1313    ///                 signing_key_id: "signing1".parse()?,
1314    ///                 key_setup: SigningKeySetup::new(
1315    ///                     KeyType::Curve25519,
1316    ///                     vec![KeyMechanism::EdDsaSignature],
1317    ///                     None,
1318    ///                     SignatureType::EdDsa,
1319    ///                     CryptographicKeyContext::OpenPgp {
1320    ///                         user_ids: OpenPgpUserIdList::new(vec![
1321    ///                             "Foobar McFooface <foobar@mcfooface.org>".parse()?,
1322    ///                         ])?,
1323    ///                         version: "v4".parse()?,
1324    ///                     },
1325    ///                 )?,
1326    ///                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIClIXZdx0aDOPcIQA+6Qx68cwSUgGTL3TWzDSX3qUEOQ user@host".parse()?,
1327    ///                 system_user: "nethsm-signing-user".parse()?,
1328    ///                 tag: "signing1".to_string(),
1329    ///             }
1330    ///         ]),
1331    ///     )?)
1332    ///     .finish()?;
1333    ///
1334    /// let nethsm_backend = NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config)?;
1335    /// # Ok(())
1336    /// # }
1337    /// ```
1338    pub fn new(
1339        nethsm: NetHsm,
1340        admin_credentials: &'a NetHsmAdminCredentials,
1341        signstar_config: &'b Config,
1342    ) -> Result<Option<Self>, crate::Error> {
1343        debug!(
1344            "Create a new NetHSM backend for Signstar config at {}",
1345            nethsm.get_url()
1346        );
1347
1348        let Some(nethsm_config) = signstar_config.nethsm() else {
1349            return Ok(None);
1350        };
1351
1352        // Ensure that the iterations of administrative credentials and signstar config match.
1353        if admin_credentials.get_iteration() != signstar_config.system().iteration() {
1354            return Err(Error::IterationMismatch {
1355                admin_creds: admin_credentials.get_iteration(),
1356                signstar_config: signstar_config.system().iteration(),
1357            }
1358            .into());
1359        }
1360
1361        // Add all available system-wide Administrators for the connection
1362        for user in admin_credentials.administrators_in_config(nethsm_config) {
1363            nethsm.add_credentials(user.into());
1364        }
1365        // Add all available namespace Administrators for the connection
1366        for user in admin_credentials.namespace_administrators_in_config(nethsm_config) {
1367            nethsm.add_credentials(user.into());
1368        }
1369        // Use the default administrator
1370        nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1371
1372        Ok(Some(Self {
1373            nethsm,
1374            admin_credentials,
1375            nethsm_config,
1376        }))
1377    }
1378
1379    /// Returns a reference to the tracked [`NetHsm`].
1380    pub fn nethsm(&self) -> &NetHsm {
1381        &self.nethsm
1382    }
1383
1384    /// Unlocks a locked [`NetHsm`] backend.
1385    pub(crate) fn unlock_nethsm(&self) -> Result<(), crate::Error> {
1386        Ok(self.nethsm.unlock(Passphrase::new(
1387            self.admin_credentials.get_unlock_passphrase().into(),
1388        ))?)
1389    }
1390
1391    /// Retrieves the state for all users on the [`NetHsm`] backend.
1392    ///
1393    /// # Note
1394    ///
1395    /// Uses the `nethsm` with the [default
1396    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
1397    ///
1398    /// # Errors
1399    ///
1400    /// Returns an error if
1401    ///
1402    /// - using the credentials of the default *R-Administrator* fails,
1403    /// - retrieving all user names of the NetHSM backend fails,
1404    /// - retrieving information about a specific NetHSM user fails,
1405    /// - or retrieving the tags of an *Operator* user fails.
1406    pub(crate) fn user_states(&self) -> Result<Vec<UserState>, crate::Error> {
1407        info!(
1408            "Retrieve all user states from NetHSM backend at \"{}\"",
1409            self.nethsm.get_url()
1410        );
1411        // Use the default R-Administrator.
1412        self.nethsm
1413            .use_credentials(&self.admin_credentials.get_default_administrator()?.name)?;
1414
1415        let users = {
1416            let mut users: Vec<UserState> = Vec::new();
1417
1418            for user_id in self.nethsm.get_users()? {
1419                let user_data = self.nethsm.get_user(&user_id)?;
1420                let tag = {
1421                    // Only Operator users can have tags assigned to them.
1422                    if user_data.role == UserRole::Operator.into() {
1423                        let user_tags = self.nethsm.get_user_tags(&user_id)?;
1424                        match user_tags.len() {
1425                            0 => None,
1426                            1 => user_tags.first().cloned(),
1427                            number => {
1428                                return Err(
1429                                    Error::UserUnexpectedNumberOfTags { user_id, number }.into()
1430                                );
1431                            }
1432                        }
1433                    } else {
1434                        None
1435                    }
1436                };
1437
1438                users.push(UserState {
1439                    name: user_id,
1440                    role: user_data.role.into(),
1441                    tag,
1442                });
1443            }
1444
1445            users
1446        };
1447
1448        Ok(users)
1449    }
1450
1451    /// Retrieves the state of a key certificate on the [`NetHsm`] backend.
1452    ///
1453    /// Key certificates may be retrieved for system-wide keys or namespaced keys.
1454    /// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1455    /// retrieved.
1456    ///
1457    /// # Note
1458    ///
1459    /// It is assumed that the current credentials for the `nethsm` provide access to the key
1460    /// certificate of key `key_id`.
1461    fn key_certificate_state(
1462        &self,
1463        key_id: &KeyId,
1464        namespace: Option<&NamespaceId>,
1465    ) -> KeyCertificateState {
1466        // Provide a dedicated string for log messages in case a namespace is used.
1467        let namespace = if let Some(namespace) = namespace {
1468            format!(" in namespace \"{namespace}\"")
1469        } else {
1470            "".to_string()
1471        };
1472        info!(
1473            "Retrieve the key certificate state for key {key_id}{namespace} from NetHSM backend at \"{}\"",
1474            self.nethsm.get_url()
1475        );
1476
1477        match self.nethsm.get_key_certificate(key_id) {
1478            Ok(Some(key_cert)) => {
1479                let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1480                    Ok((public_key, _armor_header)) => public_key,
1481                    Err(error) => {
1482                        let message = format!(
1483                            "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1484                        );
1485                        debug!("{message}");
1486                        return KeyCertificateState::NotAnOpenPgpCertificate { message };
1487                    }
1488                };
1489
1490                match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1491                    Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1492                    Err(error) => {
1493                        let message = format!(
1494                            "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1495                        );
1496                        debug!("{message}");
1497                        KeyCertificateState::NotACryptographicKeyContext { message }
1498                    }
1499                }
1500            }
1501            Ok(None) => KeyCertificateState::Empty,
1502            Err(error) => {
1503                let message = error.to_string();
1504                debug!("{message}");
1505                KeyCertificateState::Error { message }
1506            }
1507        }
1508    }
1509
1510    /// Retrieves the state for all keys on the [`NetHsm`] backend.
1511    ///
1512    /// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1513    /// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1514    ///
1515    /// # Note
1516    ///
1517    /// This function uses the `nethsm` with the [default
1518    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1519    /// namespace-specific _N-Administrator_ for individual operations.
1520    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1521    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1522    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1523    ///
1524    ///
1525    /// # Errors
1526    ///
1527    /// Returns an error if
1528    ///
1529    /// - using the default *R-Administrator* for authentication against the backend fails,
1530    /// - retrieving the names of all system-wide keys on the backend fails,
1531    /// - retrieving information on a specific system-wide key on the backend fails,
1532    /// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1533    /// - using the credentials of an *N-Administrator* fails,
1534    /// - retrieving the names of all namespaced keys on the backend fails,
1535    /// - or retrieving information on a specific namespaced key on the backend fails.
1536    pub(crate) fn key_states(&self) -> Result<Vec<KeyState>, crate::Error> {
1537        info!(
1538            "Retrieve all key states from NetHSM backend at \"{}\"",
1539            self.nethsm.get_url()
1540        );
1541        // Use the default administrator
1542        let default_admin = &self.admin_credentials.get_default_administrator()?.name;
1543        self.nethsm.use_credentials(default_admin)?;
1544
1545        let mut keys = Vec::new();
1546        // Get the state of system-wide keys.
1547        for key_id in self.nethsm.get_keys(None)? {
1548            let key = self.nethsm.get_key(&key_id)?;
1549            let key_context = self.key_certificate_state(&key_id, None);
1550            let tag = {
1551                let tags = key.restrictions.tags.unwrap_or_default();
1552                if tags.len() > 1 {
1553                    return Err(Error::KeyUnexpectedNumberOfTags {
1554                        key_id,
1555                        number: tags.len(),
1556                    }
1557                    .into());
1558                }
1559
1560                if let Some(tag) = tags.first() {
1561                    tag.clone()
1562                } else {
1563                    return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1564                }
1565            };
1566
1567            keys.push(KeyState {
1568                name: key_id,
1569                namespace: None,
1570                tag,
1571                key_type: key
1572                    .r#type
1573                    .try_into()
1574                    .map_err(nethsm::Error::SignstarCryptoKey)?,
1575                mechanisms: key
1576                    .mechanisms
1577                    .iter()
1578                    .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1579                    .collect(),
1580                key_cert_state: key_context,
1581            });
1582        }
1583
1584        let mut seen_namespaces = HashSet::new();
1585        // Get the state of namespaced keys.
1586        for user_id in self
1587            .admin_credentials
1588            .get_namespace_administrators()
1589            .iter()
1590            .map(|creds| creds.name.clone())
1591        {
1592            // Extract the namespace of the user and ensure that the namespace exists already.
1593            let Some(namespace) = user_id.namespace() else {
1594                return Err(Error::NamespaceUserNoNamespace {
1595                    user: user_id.clone(),
1596                }
1597                .into());
1598            };
1599
1600            // Only extract key information for the namespace if we have not already looked at it.
1601            if seen_namespaces.contains(namespace) {
1602                continue;
1603            }
1604            seen_namespaces.insert(namespace.clone());
1605
1606            self.nethsm.use_credentials(&user_id)?;
1607            for key_id in self.nethsm.get_keys(None)? {
1608                let key = self.nethsm.get_key(&key_id)?;
1609                let key_context = self.key_certificate_state(&key_id, Some(namespace));
1610                let tag = {
1611                    let tags = key.restrictions.tags.unwrap_or_default();
1612                    if tags.len() > 1 {
1613                        return Err(Error::KeyUnexpectedNumberOfTags {
1614                            key_id,
1615                            number: tags.len(),
1616                        }
1617                        .into());
1618                    }
1619
1620                    if let Some(tag) = tags.first() {
1621                        tag.clone()
1622                    } else {
1623                        return Err(Error::KeyUnexpectedNumberOfTags { key_id, number: 0 }.into());
1624                    }
1625                };
1626
1627                keys.push(KeyState {
1628                    name: key_id,
1629                    namespace: Some(namespace.clone()),
1630                    tag,
1631                    key_type: key
1632                        .r#type
1633                        .try_into()
1634                        .map_err(nethsm::Error::SignstarCryptoKey)?,
1635                    mechanisms: key
1636                        .mechanisms
1637                        .iter()
1638                        .filter_map(|mechanism| KeyMechanism::try_from(mechanism).ok())
1639                        .collect(),
1640                    key_cert_state: key_context,
1641                });
1642            }
1643        }
1644
1645        // Always use the default *R-Administrator* again.
1646        self.nethsm.use_credentials(default_admin)?;
1647
1648        Ok(keys)
1649    }
1650
1651    /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1652    /// non-administrative roles.
1653    ///
1654    /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1655    /// Then works down the following list to
1656    ///
1657    /// - create _R-Administrators_,
1658    ///     - or set their passphrase if they exist already,
1659    /// - create system-wide keys and add tags to them,
1660    ///     - or remove all tags from existing keys and only add the configured tags,
1661    /// - create users in the system-wide, non-administrative roles (i.e.
1662    ///   [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1663    ///   [`Operator`][`UserRole::Operator`]),
1664    ///     - or set their passphrase if they exist already,
1665    /// - create OpenPGP certificates for system-wide keys,
1666    ///     - or do nothing if they exist already,
1667    /// - create _N-Administrators_ and their respective namespaces,
1668    ///     - or set their passphrase if they exist already,
1669    /// - create namespaced keys and add tags to them,
1670    ///     - or remove all tags from existing keys and only add the configured tags,
1671    /// - create users in the namespaced, non-administrative roles (i.e.
1672    ///   [`Operator`][`UserRole::Operator`]),
1673    ///     - or set their passphrase if they exist already,
1674    /// - and create OpenPGP certificates for namespaced keys,
1675    ///     - or do nothing if they exist already.
1676    ///
1677    /// # Note
1678    ///
1679    /// This function uses the `nethsm` with the [default
1680    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1681    /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1682    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1683    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1684    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1685    /// non-administrative user.
1686    ///
1687    /// # Errors
1688    ///
1689    /// Returns an error if
1690    ///
1691    /// - retrieving the state of the [`NetHsm`] backend fails,
1692    /// - provisioning an unprovisioned [`NetHsm`] fails,
1693    /// - unlocking a locked [`NetHsm`] backend fails,
1694    /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1695    /// - adding system-wide keys fails,
1696    /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1697    ///   [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1698    /// - adding OpenPGP certificates for system-wide keys fails,
1699    /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1700    ///   their respective namespace fails,
1701    /// - adding namespaced keys fails,
1702    /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1703    /// - or adding OpenPGP certificates for namespaced keys fails.
1704    pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1705        debug!(
1706            "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1707            self.nethsm.get_url()
1708        );
1709
1710        // Extract user mappings for non-administrative users.
1711        let non_admin_users = self
1712            .nethsm_config
1713            .mappings()
1714            .iter()
1715            .filter(|mapping| !matches!(mapping, NetHsmUserMapping::Admin(..)))
1716            .collect::<Vec<_>>();
1717
1718        match self.nethsm.state()? {
1719            SystemState::Unprovisioned => {
1720                debug!(
1721                    "Unprovisioned NetHSM backend detected at {}",
1722                    self.nethsm.get_url()
1723                );
1724
1725                self.nethsm.provision(
1726                    Passphrase::from_str(self.admin_credentials.get_unlock_passphrase()).map_err(
1727                        |source| {
1728                            crate::Error::NetHsm(nethsm::Error::SignstarCryptoPassphrase(source))
1729                        },
1730                    )?,
1731                    self.admin_credentials
1732                        .get_default_administrator()?
1733                        .passphrase
1734                        .clone(),
1735                    nethsm::Utc::now(),
1736                )?;
1737            }
1738            SystemState::Locked => {
1739                debug!(
1740                    "Locked NetHSM backend detected at {}",
1741                    self.nethsm.get_url()
1742                );
1743
1744                self.nethsm.unlock(Passphrase::new(
1745                    self.admin_credentials.get_unlock_passphrase().into(),
1746                ))?;
1747            }
1748            SystemState::Operational => {
1749                debug!(
1750                    "Operational NetHSM backend detected at {}",
1751                    self.nethsm.get_url()
1752                );
1753            }
1754        }
1755
1756        // Add any missing users and keys.
1757        add_system_wide_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1758        add_system_wide_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1759        add_non_administrative_users(
1760            &self.nethsm,
1761            self.admin_credentials,
1762            &non_admin_users,
1763            user_credentials,
1764        )?;
1765        add_system_wide_openpgp_certificates(
1766            &self.nethsm,
1767            self.admin_credentials,
1768            &non_admin_users,
1769        )?;
1770        add_namespace_admins(&self.nethsm, self.admin_credentials, self.nethsm_config)?;
1771        add_namespaced_keys(&self.nethsm, self.admin_credentials, &non_admin_users)?;
1772        add_namespaced_non_administrative_users(
1773            &self.nethsm,
1774            self.admin_credentials,
1775            &non_admin_users,
1776            user_credentials,
1777        )?;
1778        add_namespaced_openpgp_certificates(
1779            &self.nethsm,
1780            self.admin_credentials,
1781            &non_admin_users,
1782        )?;
1783
1784        Ok(())
1785    }
1786}
1787
1788/// The state of a user in a [`NetHsmBackend`].
1789#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1790pub(crate) struct UserState {
1791    /// The name of the user.
1792    pub name: UserId,
1793    /// The role of the user.
1794    pub role: UserRole,
1795    /// The optional tag assigned to the user.
1796    pub tag: Option<String>,
1797}
1798
1799impl Display for UserState {
1800    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1801        write!(f, "{} (role: {}", self.name, self.role)?;
1802        if let Some(tag) = self.tag.as_ref() {
1803            write!(f, "; tag: {tag}")?;
1804        }
1805        write!(f, ")")?;
1806
1807        Ok(())
1808    }
1809}
1810
1811/// The state of a key in a [`NetHsmBackend`].
1812#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1813pub(crate) struct KeyState {
1814    /// The name of the key.
1815    pub name: KeyId,
1816    /// The optional namespace the key is used in.
1817    pub namespace: Option<NamespaceId>,
1818    /// The tag assigned to the key.
1819    pub tag: String,
1820    /// The key type of the key.
1821    pub key_type: KeyType,
1822    /// The mechanisms supported by the key.
1823    pub mechanisms: Vec<KeyMechanism>,
1824    /// The context in which the key is used.
1825    pub key_cert_state: KeyCertificateState,
1826}
1827
1828impl Display for KeyState {
1829    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1830        write!(f, "{} (", self.name)?;
1831        if let Some(namespace) = self.namespace.as_ref() {
1832            write!(f, "namespace: {namespace}; ")?;
1833        }
1834        write!(f, "tag: {}; ", self.tag)?;
1835        write!(f, "type: {}; ", self.key_type)?;
1836        write!(
1837            f,
1838            "mechanisms: {}; ",
1839            self.mechanisms
1840                .iter()
1841                .map(|mechanism| mechanism.to_string())
1842                .collect::<Vec<String>>()
1843                .join(", ")
1844        )?;
1845        write!(f, "context: {}", self.key_cert_state)?;
1846        write!(f, ")")?;
1847
1848        Ok(())
1849    }
1850}
1851
1852/// The state of a [`NetHsmBackend`].
1853///
1854/// This tracks the available backend users, their roles and assigned tags, and the key setups
1855/// associated with users.
1856#[derive(Debug, Eq, PartialEq)]
1857pub struct NetHsmBackendState {
1858    /// The user states.
1859    pub(crate) user_states: Vec<UserState>,
1860    /// The key states.
1861    pub(crate) key_states: Vec<KeyState>,
1862}
1863
1864impl NetHsmBackendState {
1865    /// The name of the origin for the state.
1866    pub const STATE_NAME: &'static str = "NetHSM backend";
1867}
1868
1869impl StateOriginInfo for NetHsmBackendState {
1870    fn state_name(&self) -> &str {
1871        Self::STATE_NAME
1872    }
1873
1874    fn state_origin(&self) -> StateOrigin {
1875        StateOrigin::Backend
1876    }
1877}
1878
1879impl<'a, 'b> TryFrom<&NetHsmBackend<'a, 'b>> for NetHsmBackendState {
1880    type Error = crate::Error;
1881
1882    /// Creates a new [`NetHsmBackendState`] from a [`NetHsmBackend`].
1883    ///
1884    /// # Note
1885    ///
1886    /// Uses the [`NetHsm`] backend with the [default
1887    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1888    /// namespace-specific _N-Administrator_ for individual operations.
1889    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1890    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1891    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1892    ///
1893    /// # Errors
1894    ///
1895    /// Returns an error if
1896    ///
1897    /// - retrieving the system state of the [`NetHsm`] backend fails,
1898    /// - unlocking a locked [`NetHsm`] backend fails,
1899    /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
1900    fn try_from(value: &NetHsmBackend<'a, 'b>) -> Result<Self, Self::Error> {
1901        debug!(
1902            "Retrieve state of the NetHSM backend at {}",
1903            value.nethsm().get_url()
1904        );
1905
1906        let (user_states, key_states) = match value.nethsm().state()? {
1907            SystemState::Unprovisioned => {
1908                debug!(
1909                    "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
1910                    value.nethsm().get_url()
1911                );
1912
1913                (Vec::new(), Vec::new())
1914            }
1915            SystemState::Locked => {
1916                debug!(
1917                    "Locked NetHSM backend detected at {}",
1918                    value.nethsm().get_url()
1919                );
1920
1921                value.unlock_nethsm()?;
1922
1923                let user_states = value.user_states()?;
1924                let key_states = value.key_states()?;
1925
1926                (user_states, key_states)
1927            }
1928            SystemState::Operational => {
1929                debug!(
1930                    "Operational NetHSM backend detected at {}",
1931                    value.nethsm().get_url()
1932                );
1933
1934                let user_states = value.user_states()?;
1935                let key_states = value.key_states()?;
1936
1937                (user_states, key_states)
1938            }
1939        };
1940
1941        Ok(Self {
1942            user_states,
1943            key_states,
1944        })
1945    }
1946}
1947
1948#[cfg(test)]
1949#[cfg(feature = "_test-helpers")]
1950mod tests {
1951    use log::LevelFilter;
1952    use nethsm::{
1953        Connection,
1954        ConnectionSecurity,
1955        CryptographicKeyContext,
1956        FullCredentials,
1957        NetHsm,
1958        OpenPgpUserIdList,
1959        OpenPgpVersion,
1960        UserRole,
1961    };
1962    use rstest::rstest;
1963    use signstar_common::logging::setup_logging;
1964    use testresult::TestResult;
1965
1966    use super::*;
1967    use crate::test::{ConfigFileConfig, ConfigFileVariant, SystemPrepareConfig};
1968
1969    /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1970    /// [`NetHsmAdminCredentials`] and [`Config`].
1971    #[test]
1972    fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1973        setup_logging(LevelFilter::Debug)?;
1974
1975        let prepare_config = SystemPrepareConfig {
1976            machine_id: false,
1977            credentials_socket: false,
1978            signstar_config: ConfigFileConfig {
1979                location: None,
1980                variant: ConfigFileVariant::OnlyNetHsmBackendAdminPlaintextNonAdminSystemdCreds,
1981                system_user_config: None,
1982            },
1983        };
1984        let signstar_config = prepare_config.signstar_config.variant.to_config()?;
1985
1986        let nethsm = NetHsm::new(
1987            Connection::new(
1988                "https://example.org/api/v1".try_into()?,
1989                ConnectionSecurity::Unsafe,
1990            ),
1991            None,
1992            None,
1993            None,
1994        )?;
1995        // The administrative credentials.
1996        let admin_credentials = NetHsmAdminCredentials::new(
1997            // this is different from the one in the Signstar config.
1998            2,
1999            "backup-passphrase".parse()?,
2000            "unlock-passphrase".parse()?,
2001            vec![FullCredentials::new(
2002                "admin".parse()?,
2003                "admin-passphrase".parse()?,
2004            )],
2005            vec![FullCredentials::new(
2006                "ns1~admin".parse()?,
2007                "ns1-admin-passphrase".parse()?,
2008            )],
2009        )?;
2010        let nethsm_backend_result =
2011            NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
2012
2013        assert!(
2014            nethsm_backend_result.is_err(),
2015            "Test should have failed, but succeeded"
2016        );
2017        assert!(
2018            matches!(
2019                nethsm_backend_result,
2020                Err(crate::Error::NetHsmBackend(Error::IterationMismatch {
2021                    admin_creds: _,
2022                    signstar_config: _
2023                }))
2024            ),
2025            "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
2026        );
2027
2028        Ok(())
2029    }
2030
2031    /// Ensures that [`UserState::to_string`] shows correctly.
2032    #[rstest]
2033    #[case(
2034        UserState{
2035            name: "testuser".parse()?,
2036            role: UserRole::Operator,
2037            tag: Some("tag1".to_string())
2038        },
2039        "testuser (role: Operator; tag: tag1)",
2040    )]
2041    #[case(
2042        UserState{
2043            name: "testuser".parse()?,
2044            role: UserRole::Operator,
2045            tag: None,
2046        },
2047        "testuser (role: Operator)",
2048    )]
2049    #[case(
2050        UserState{
2051            name: "testuser".parse()?,
2052            role: UserRole::Metrics,
2053            tag: None,
2054        },
2055        "testuser (role: Metrics)",
2056    )]
2057    #[case(
2058        UserState{
2059            name: "testuser".parse()?,
2060            role: UserRole::Backup,
2061            tag: None,
2062        },
2063        "testuser (role: Backup)",
2064    )]
2065    #[case(
2066        UserState{name:
2067            "testuser".parse()?,
2068            role: UserRole::Administrator,
2069            tag: None,
2070        },
2071        "testuser (role: Administrator)",
2072    )]
2073    fn user_state_to_string(#[case] user_state: UserState, #[case] expected: &str) -> TestResult {
2074        setup_logging(LevelFilter::Debug)?;
2075
2076        assert_eq!(user_state.to_string(), expected);
2077        Ok(())
2078    }
2079
2080    /// Ensures that [`KeyState::to_string`] shows correctly.
2081    #[rstest]
2082    #[case::namespaced_key_with_openpgp_v4_cert(
2083        KeyState{
2084            name: "key1".parse()?,
2085            namespace: Some("ns1".parse()?),
2086            tag: "tag1".to_string(),
2087            key_type: KeyType::Curve25519,
2088            mechanisms: vec![KeyMechanism::EdDsaSignature],
2089            key_cert_state: KeyCertificateState::KeyContext(
2090                CryptographicKeyContext::OpenPgp {
2091                    user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
2092                    version: OpenPgpVersion::V4,
2093                })
2094        },
2095        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: OpenPGP (Version: 4; User IDs: \"John Doe <john@example.org>\"))",
2096    )]
2097    #[case::namespaced_key_with_raw_cert(
2098        KeyState{
2099            name: "key1".parse()?,
2100            namespace: Some("ns1".parse()?),
2101            tag: "tag1".to_string(),
2102            key_type: KeyType::Curve25519,
2103            mechanisms: vec![KeyMechanism::EdDsaSignature],
2104            key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2105        },
2106        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2107    )]
2108    #[case::namespaced_key_with_no_cert(
2109        KeyState{
2110            name: "key1".parse()?,
2111            namespace: Some("ns1".parse()?),
2112            tag: "tag1".to_string(),
2113            key_type: KeyType::Curve25519,
2114            mechanisms: vec![KeyMechanism::EdDsaSignature],
2115            key_cert_state: KeyCertificateState::Empty
2116        },
2117        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Empty)",
2118    )]
2119    #[case::namespaced_key_with_cert_error(
2120        KeyState{
2121            name: "key1".parse()?,
2122            namespace: Some("ns1".parse()?),
2123            tag: "tag1".to_string(),
2124            key_type: KeyType::Curve25519,
2125            mechanisms: vec![KeyMechanism::EdDsaSignature],
2126            key_cert_state: KeyCertificateState::Error { message: "the dog ate it".to_string() }
2127        },
2128        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Error retrieving key certificate - the dog ate it)",
2129    )]
2130    #[case::namespaced_key_with_not_a_cert_context(
2131        KeyState{
2132            name: "key1".parse()?,
2133            namespace: Some("ns1".parse()?),
2134            tag: "tag1".to_string(),
2135            key_type: KeyType::Curve25519,
2136            mechanisms: vec![KeyMechanism::EdDsaSignature],
2137            key_cert_state: KeyCertificateState::NotACryptographicKeyContext { message: "failed to convert".to_string() }
2138        },
2139        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not a cryptographic key context - \"failed to convert\")",
2140    )]
2141    #[case::namespaced_key_with_not_an_openpgp_cert(
2142        KeyState{
2143            name: "key1".parse()?,
2144            namespace: Some("ns1".parse()?),
2145            tag: "tag1".to_string(),
2146            key_type: KeyType::Curve25519,
2147            mechanisms: vec![KeyMechanism::EdDsaSignature],
2148            key_cert_state: KeyCertificateState::NotAnOpenPgpCertificate { message: "it's a blob".to_string() }
2149        },
2150        "key1 (namespace: ns1; tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Not an OpenPGP certificate - \"it's a blob\")",
2151    )]
2152    #[case::system_wide_key_with_no_cert_and_no_tags_and_raw_cert(
2153        KeyState{
2154            name: "key1".parse()?,
2155            namespace: None,
2156            tag: "tag1".to_string(),
2157            key_type: KeyType::Curve25519,
2158            mechanisms: vec![KeyMechanism::EdDsaSignature],
2159            key_cert_state: KeyCertificateState::KeyContext(CryptographicKeyContext::Raw)
2160        },
2161        "key1 (tag: tag1; type: Curve25519; mechanisms: EdDsaSignature; context: Raw)",
2162    )]
2163    fn key_state_to_string(#[case] key_state: KeyState, #[case] expected: &str) -> TestResult {
2164        setup_logging(LevelFilter::Debug)?;
2165
2166        assert_eq!(key_state.to_string(), expected);
2167        Ok(())
2168    }
2169}