Skip to main content

signstar_config/nethsm/
backend.rs

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