signstar_config/nethsm/
backend.rs

1//! Backend handling for [`NetHsm`].
2//!
3//! Based on a [`NetHsm`], [`NetHsmAdminCredentials`] and a [`SignstarConfig`] 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 [`SignstarConfig`]
7//! are created and adapted to changes upon re-run.
8//! With the help of [`NetHsmBackend::state`] the current [`State`] of a [`NetHsm`] backend can
9//! be created and compared with e.g. the [`State`] representation of a [`SignstarConfig`].
10//!
11//! # Note
12//!
13//! This module only works with data for the same iteration (i.e. the iteration of the
14//! [`NetHsmAdminCredentials`] and those of the [`NetHsm`] backend must match).
15
16use std::{collections::HashSet, str::FromStr};
17
18use log::{debug, trace, warn};
19use nethsm::{
20    CryptographicKeyContext,
21    FullCredentials,
22    KeyId,
23    KeyMechanism,
24    KeyType,
25    NamespaceId,
26    NetHsm,
27    OpenPgpKeyUsageFlags,
28    Passphrase,
29    SystemState,
30    UserId,
31    UserRole,
32    Utc,
33};
34use pgp::composed::{Deserializable, SignedPublicKey};
35
36use super::{Error, state::StateType};
37use crate::{
38    FilterUserKeys,
39    KeyState,
40    NetHsmAdminCredentials,
41    SignstarConfig,
42    State,
43    UserMapping,
44    UserState,
45    nethsm::state::KeyCertificateState,
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::get_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) -> Result<(), crate::Error> {
71    debug!(
72        "Setup system-wide administrators (R-Administrators) on NetHSM backend at {}",
73        nethsm.get_url()
74    );
75
76    let default_admin = &admin_credentials.get_default_administrator()?.name;
77    nethsm.use_credentials(default_admin)?;
78    let available_users = nethsm.get_users()?;
79    trace!(
80        "Available users on NetHSM: {}",
81        available_users
82            .iter()
83            .map(|user| user.to_string())
84            .collect::<Vec<_>>()
85            .join(", ")
86    );
87
88    for user in admin_credentials.get_administrators().iter() {
89        // Only add if user doesn't exist yet, else set passphrase
90        if !available_users.contains(&user.name) {
91            nethsm.add_user(
92                format!("System-wide Admin {}", user.name),
93                UserRole::Administrator,
94                user.passphrase.clone(),
95                Some(user.name.clone()),
96            )?;
97        } else {
98            nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
99        }
100    }
101    Ok(())
102}
103
104/// Retrieves the first available user in the [`Administrator`][`UserRole::Administrator`]
105/// (*N-Administrator*) role in a namespace.
106///
107/// Derives a list of users in the [`Administrator`][`UserRole::Administrator`] role in `namespace`
108/// from `available_users`.
109/// Ensures that at least one of the users is available on the `nethsm`.
110///
111/// # Errors
112///
113/// Returns an error if
114/// - user information of an *N-Administrator* cannot be retrieved,
115/// - or no *N-Administrator* is available in the `namespace`.
116fn get_first_available_namespace_admin(
117    nethsm: &NetHsm,
118    admin_credentials: &NetHsmAdminCredentials,
119    available_users: &[UserId],
120    namespace: &NamespaceId,
121) -> Result<UserId, crate::Error> {
122    debug!("Get the first available N-Administrator in namespace \"{namespace}\"");
123
124    // Retrieve the list of users that are both in the namespace and match an entry in the list of
125    // N-Administrators in the administrative credentials.
126    let namespace_admins = available_users
127        .iter()
128        .filter(|user| {
129            user.namespace() == Some(namespace)
130                && admin_credentials
131                    .get_namespace_administrators()
132                    .iter()
133                    .any(|creds| &creds.name == *user)
134        })
135        .cloned()
136        .collect::<Vec<UserId>>();
137
138    let mut checked_namespace_admins = Vec::new();
139    for namespace_admin in namespace_admins {
140        if Into::<UserRole>::into(nethsm.get_user(&namespace_admin)?.role)
141            == UserRole::Administrator
142        {
143            checked_namespace_admins.push(namespace_admin);
144        }
145    }
146
147    debug!(
148        "All N-Administrators in namespace \"{namespace}\": {}",
149        checked_namespace_admins
150            .iter()
151            .map(|user| user.to_string())
152            .collect::<Vec<String>>()
153            .join(", ")
154    );
155
156    if checked_namespace_admins.is_empty() {
157        return Err(Error::NamespaceHasNoAdmin {
158            namespace: namespace.clone(),
159            url: nethsm.get_url(),
160        }
161        .into());
162    }
163
164    // Select the first N-Administrator in the namespace.
165    let Some(admin) = checked_namespace_admins.first() else {
166        return Err(Error::NamespaceHasNoAdmin {
167            namespace: namespace.clone(),
168            url: nethsm.get_url(),
169        }
170        .into());
171    };
172
173    Ok(admin.clone())
174}
175
176/// Sets up all _N-Administrators_ and their respective namespaces.
177///
178/// # Note
179///
180/// This function uses the `nethsm` with the [default
181/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
182/// namespace-specific _N-Administrator_ for individual operations.
183/// If this function succeeds, the `nethsm` is guaranteed to use the [default
184/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
185/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
186///
187/// # Errors
188///
189/// Returns an error if
190///
191/// - user information cannot be retrieved from the `nethsm`,
192/// - the available namespaces cannot be retrieved from the `nethsm`,
193/// - one of the N-Administrators in the `admin_credentials` is not in a namespace,
194/// - a namespace exists already, but no known N-Administrator is available for it,
195/// - an N-Administrator and its namespace exist already, but that user's passphrase cannot be set,
196/// - an N-Administrator does not yet exist and cannot be added,
197/// - a namespace does not yet exist and cannot be added,
198/// - or switching back to the default R-Administrator credentials fails.
199fn add_namespace_admins(
200    nethsm: &NetHsm,
201    admin_credentials: &NetHsmAdminCredentials,
202) -> Result<(), crate::Error> {
203    debug!(
204        "Setup namespace administrators (N-Administrators) on NetHSM backend at {}",
205        nethsm.get_url()
206    );
207
208    // Use the default R-Administrator for authentication to the backend by default.
209    let default_admin = &admin_credentials.get_default_administrator()?.name;
210    nethsm.use_credentials(default_admin)?;
211
212    let available_users = nethsm.get_users()?;
213    trace!(
214        "The available users on the NetHSM backend at {} are: {}",
215        nethsm.get_url(),
216        available_users
217            .iter()
218            .map(|user| user.to_string())
219            .collect::<Vec<String>>()
220            .join(", ")
221    );
222    let available_namespaces = nethsm.get_namespaces()?;
223    trace!(
224        "The available namespaces on the NetHSM backend at {} are: {}",
225        nethsm.get_url(),
226        available_namespaces
227            .iter()
228            .map(|namespace| namespace.to_string())
229            .collect::<Vec<String>>()
230            .join(", ")
231    );
232
233    // Extract the namespace from each namespace administrator found in the administrative
234    // credentials.
235    for user in admin_credentials.get_namespace_administrators() {
236        let Some(namespace) = user.name.namespace() else {
237            return Err(Error::NamespaceAdminHasNoNamespace {
238                user: user.name.clone(),
239            }
240            .into());
241        };
242
243        let namespace_exists = available_namespaces.contains(namespace);
244        if namespace_exists {
245            // Select the first available N-Administrator credentials for interacting with the
246            // NetHSM backend.
247            // This might be the targeted user itself!
248            nethsm.use_credentials(&get_first_available_namespace_admin(
249                nethsm,
250                admin_credentials,
251                &available_users,
252                namespace,
253            )?)?;
254        }
255
256        // If the list of available users on the NetHSM does not include the given N-Administrator,
257        // we create the user.
258        if available_users.contains(&user.name) {
259            // Set the passphrase of the user.
260            nethsm.set_user_passphrase(user.name.clone(), user.passphrase.clone())?;
261        } else {
262            nethsm.add_user(
263                format!("Namespace Admin {}", user.name),
264                UserRole::Administrator,
265                user.passphrase.clone(),
266                Some(user.name.clone()),
267            )?;
268
269            // If the namespace does not yet exist add the namespace (authenticated as the default
270            // R-Administrator).
271            if !namespace_exists {
272                nethsm.add_namespace(namespace)?;
273            }
274        }
275        // Always use the default R-Administrator again.
276        nethsm.use_credentials(default_admin)?;
277    }
278
279    Ok(())
280}
281
282/// Sets up all system-wide, non-administrative users based on provided credentials.
283///
284/// # Note
285///
286/// It is assumed that the [default
287/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and system-wide keys are
288/// already set up, before calling this function (see `add_system_wide_admins` and
289/// `add_system_wide_keys`, respectively).
290///
291/// This function uses the `nethsm` with the [default
292/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] and is guaranteed to do
293/// so when it finishes.
294///
295/// # Errors
296///
297/// Returns an error if
298///
299/// - there are no matching credentials in `user_credentials` for a user in the list of all
300///   available system-wide, non-administrative users,
301/// - a user exists already, but its passphrase cannot be set,
302/// - a user does not yet exist and it cannot be added,
303/// - a user has a tag and deleting it fails,
304/// - or adding a tag to a user fails.
305fn add_non_administrative_users(
306    nethsm: &NetHsm,
307    admin_credentials: &NetHsmAdminCredentials,
308    users: &[UserMapping],
309    user_credentials: &[FullCredentials],
310) -> Result<(), crate::Error> {
311    debug!(
312        "Setup non-administrative, system-wide users on NetHSM backend at {}",
313        nethsm.get_url()
314    );
315
316    let default_admin = &admin_credentials.get_default_administrator()?.name;
317    nethsm.use_credentials(default_admin)?;
318    let available_users = nethsm.get_users()?;
319    debug!("Available users: {available_users:?}");
320
321    for mapping in users.iter() {
322        // Add all users of the mapping.
323        for (user, role, tags) in mapping
324            .get_nethsm_user_role_and_tags()
325            .iter()
326            // Only use system-wide, non-administrative users.
327            .filter(|(user, role, _tags)| !user.is_namespaced() && role != &UserRole::Administrator)
328        {
329            let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
330                return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
331            };
332
333            if available_users.contains(user) {
334                nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
335            } else {
336                nethsm.add_user(
337                    format!("{role} user {user}"),
338                    *role,
339                    creds.passphrase.clone(),
340                    Some(user.clone()),
341                )?;
342            }
343
344            // Add tags to users.
345            for tag in tags {
346                for available_tag in nethsm.get_user_tags(user)? {
347                    nethsm.delete_user_tag(user, available_tag.as_str())?;
348                }
349                nethsm.add_user_tag(user, tag.as_str())?;
350            }
351        }
352    }
353
354    Ok(())
355}
356
357/// Sets up all namespaced non-administrative users.
358///
359/// # Note
360///
361/// It is assumed that _N-Administrators_ and namespaced keys are already set up, before calling
362/// this function (see `add_namespace_admins` and `add_namespaced_keys`, respectively).
363///
364/// This function uses the `nethsm` with the [default
365/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
366/// namespace-specific _N-Administrator_ for individual operations.
367/// If this function succeeds, the `nethsm` is guaranteed to use the [default
368/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
369/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
370///
371/// # Errors
372///
373/// Returns an error if
374///
375/// - a namespaced user is not in a namespace,
376/// - the namespace of a user does not exist,
377/// - the namespace of a user exists, but no usable *N-Administrator* for it is known,
378/// - there are no matching credentials in `user_credentials` for a user in the list of all,
379/// - a user exists already, but its passphrase cannot be set,
380/// - a user does not yet exist and cannot be created,
381/// - a tag cannot be removed from a user,
382/// - or a tag cannot be added to a user.
383fn add_namespaced_non_administrative_users(
384    nethsm: &NetHsm,
385    admin_credentials: &NetHsmAdminCredentials,
386    users: &[UserMapping],
387    user_credentials: &[FullCredentials],
388) -> Result<(), crate::Error> {
389    debug!(
390        "Setup non-administrative, namespaced users on NetHSM backend at {}",
391        nethsm.get_url()
392    );
393
394    // Use the default R-Administrator for authentication to the backend by default.
395    let default_admin = &admin_credentials.get_default_administrator()?.name;
396    nethsm.use_credentials(default_admin)?;
397
398    let available_users = nethsm.get_users()?;
399    let available_namespaces = nethsm.get_namespaces()?;
400
401    for mapping in users.iter() {
402        for (user, role, tags) in mapping
403            .get_nethsm_user_role_and_tags()
404            .iter()
405            // Only use non-administrative, namespaced users on the NetHSM backend.
406            .filter(|(user, role, _tags)| user.is_namespaced() && role != &UserRole::Administrator)
407        {
408            // Extract the namespace of the user and ensure that the namespace exists already.
409            let Some(namespace) = user.namespace() else {
410                return Err(Error::NamespaceUserNoNamespace { user: user.clone() }.into());
411            };
412            if !available_namespaces.contains(namespace) {
413                return Err(Error::NamespaceMissing {
414                    namespace: namespace.clone(),
415                }
416                .into());
417            }
418
419            // Select the first available N-Administrator credentials for interacting with the
420            // NetHSM backend.
421            nethsm.use_credentials(&get_first_available_namespace_admin(
422                nethsm,
423                admin_credentials,
424                &available_users,
425                namespace,
426            )?)?;
427
428            // Retrieve credentials for the specific user.
429            let Some(creds) = user_credentials.iter().find(|creds| &creds.name == user) else {
430                return Err(Error::UserMissingPassphrase { user: user.clone() }.into());
431            };
432
433            // If the user exists already, only set its passphrase, otherwise create it.
434            if available_users.contains(user) {
435                nethsm.set_user_passphrase(user.clone(), creds.passphrase.clone())?;
436            } else {
437                nethsm.add_user(
438                    format!("{role} user {user}"),
439                    *role,
440                    creds.passphrase.clone(),
441                    Some(user.clone()),
442                )?;
443            }
444
445            // Delete all tags of the user.
446            let available_tags = nethsm.get_user_tags(user)?;
447            for available_tag in available_tags {
448                nethsm.delete_user_tag(user, available_tag.as_str())?;
449            }
450            // Setup tags for the user.
451            for tag in tags.iter() {
452                nethsm.add_user_tag(user, tag)?;
453            }
454        }
455    }
456    // Always use the default R-Administrator again.
457    nethsm.use_credentials(default_admin)?;
458
459    Ok(())
460}
461
462/// Comparable components of a key setup between a [`NetHsm`] backend and a Signstar config.
463struct KeySetupComparison {
464    /// The type of state, that the data originates from.
465    pub state_type: StateType,
466    /// The key type of the setup.
467    pub key_type: KeyType,
468    /// The key mechanisms of the setup.
469    pub key_mechanisms: HashSet<KeyMechanism>,
470}
471
472/// Compares the key setups of a key from a Signstar config and that of a NetHSM backend.
473///
474/// Compares the [`KeyType`] and [`KeyMechanism`]s of `key_setup_a` and `key_setup_b`, which both
475/// have to be identical.
476///
477/// Emits a warning if the [`KeyType`] or list of [`KeyMechanism`]s of `key_setup_a` and
478/// `key_setup_b` do not match.
479fn compare_key_setups(
480    key_id: &KeyId,
481    namespace: Option<&NamespaceId>,
482    key_setup_a: KeySetupComparison,
483    key_setup_b: KeySetupComparison,
484) {
485    let namespace = if let Some(namespace) = namespace {
486        format!(" in namespace \"{namespace}\"")
487    } else {
488        "".to_string()
489    };
490    debug!(
491        "Compare key setup of key \"{key_id}\"{namespace} in {} (A) and {} (B)",
492        key_setup_a.state_type, key_setup_b.state_type
493    );
494
495    // Compare key type and warn about mismatches.
496    if key_setup_b.key_type != key_setup_a.key_type {
497        warn!(
498            "Key type mismatch of key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B) backend: {}!",
499            key_setup_a.state_type,
500            key_setup_a.key_type,
501            key_setup_b.state_type,
502            key_setup_b.key_type
503        );
504    }
505
506    // Compare key mechanisms and warn about mismatches.
507    if key_setup_b.key_mechanisms != key_setup_a.key_mechanisms {
508        warn!(
509            "Key mechanisms mismatch for key \"{key_id}\"{namespace}:\n{} (A): {}\n{} (B): {}!",
510            key_setup_a.state_type,
511            key_setup_a
512                .key_mechanisms
513                .iter()
514                .map(|mechanism| mechanism.to_string())
515                .collect::<Vec<String>>()
516                .join(", "),
517            key_setup_b.state_type,
518            key_setup_b
519                .key_mechanisms
520                .iter()
521                .map(|mechanism| mechanism.to_string())
522                .collect::<Vec<String>>()
523                .join(", "),
524        );
525    }
526}
527
528/// Sets up all system-wide keys.
529///
530/// Creates any missing keys and adds the configured tags for all of them.
531/// If keys exist already, deletes all tags and adds the configured ones for them.
532///
533/// # Note
534///
535/// It is assumed that all required _R-Administrators_ have already been set up (see
536/// `add_system_wide_admins`) before calling this function.
537///
538/// This function uses the `nethsm` with the [default
539/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
540///
541/// This function does not fail on mismatching keys, as it is assumed that keys are added
542/// intentionally and should not be deleted or altered.
543/// However, warnings are emitted if an existing key has a mismatching [`KeyType`] or
544/// [`KeyMechanisms`][`KeyMechanism`] from what is configured in the Signstar configuration file.
545///
546/// # Errors
547///
548/// Returns an error if
549///
550/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
551/// - the list of available keys on the NetHSM backend cannot be retrieved,
552/// - information about a single key cannot be retrieved from the NetHSM backend,
553/// - if a tag cannot be removed from an existing key,
554/// - if a tag cannot be added to an existing key,
555/// - or if a missing key cannot be created.
556fn add_system_wide_keys(
557    nethsm: &NetHsm,
558    admin_credentials: &NetHsmAdminCredentials,
559    users: &[UserMapping],
560) -> Result<(), crate::Error> {
561    debug!(
562        "Setup system-wide cryptographic keys on NetHSM backend at {}",
563        nethsm.get_url()
564    );
565
566    // Use the default R-Administrator for authentication to the backend by default.
567    let default_admin = &admin_credentials.get_default_administrator()?.name;
568    nethsm.use_credentials(default_admin)?;
569
570    let available_keys = nethsm.get_keys(None)?;
571
572    for mapping in users {
573        for (_user, key_setup, tag) in
574            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
575        {
576            let key_id = key_setup.get_key_id();
577            if available_keys.contains(&key_setup.get_key_id()) {
578                // Retrieve information about the key.
579                let info = nethsm.get_key(&key_id)?;
580
581                // Compare the key setups.
582                compare_key_setups(
583                    &key_id,
584                    None,
585                    KeySetupComparison {
586                        state_type: StateType::SignstarConfig,
587                        key_type: key_setup.get_key_type(),
588                        key_mechanisms: HashSet::from_iter(key_setup.get_key_mechanisms()),
589                    },
590                    KeySetupComparison {
591                        state_type: StateType::NetHsm,
592                        key_type: info.r#type.into(),
593                        key_mechanisms: info.mechanisms.iter().map(Into::into).collect(),
594                    },
595                );
596
597                // Remove all existing tags.
598                if let Some(available_tags) = info.restrictions.tags {
599                    for available_tag in available_tags {
600                        nethsm.delete_key_tag(&key_id, available_tag.as_str())?;
601                    }
602                }
603                // Add the required tag to the key.
604                nethsm.add_key_tag(&key_id, tag.as_str())?;
605            } else {
606                // Add the key, including the required tag.
607                nethsm.generate_key(
608                    key_setup.get_key_type(),
609                    key_setup.get_key_mechanisms(),
610                    key_setup.get_key_length(),
611                    Some(key_id.clone()),
612                    Some(vec![tag]),
613                )?;
614            }
615        }
616    }
617
618    Ok(())
619}
620
621/// Sets up all namespaced keys and tags them.
622///
623/// Creates any missing keys and adds the configured tags for all of them.
624/// If keys exist already, deletes all tags and adds the configured ones for them.
625///
626/// # Note
627///
628/// It is assumed that _N-Administrators_ have already been set up, before calling
629/// this function (see `add_namespace_admins`).
630///
631/// This function uses the `nethsm` with the [default
632/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
633/// namespace-specific _N-Administrator_ for individual operations.
634/// If this function succeeds, the `nethsm` is guaranteed to use the [default
635/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
636/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
637///
638/// This function does not fail on mismatching keys, as it is assumed that keys are added
639/// intentionally and should not be deleted/altered.
640/// However, warnings are emitted if an existing key has a mismatching key type or key mechanisms
641/// from what is configured in the Signstar configuration file.
642///
643/// Opposite to the behavior of `add_system_wide_keys`, this function does not delete any tags from
644/// keys.
645/// This is due to [a bug in the NetHSM firmware], which leads to a crash when adding a tag to a
646/// key, trying to remove and then re-adding it again.
647///
648/// # Errors
649///
650/// Returns an error if
651///
652/// - the default system-wide *R-Administrator* cannot be retrieved or used for authentication,
653/// - retrieving the list of available users from the NetHSM backend fails,
654/// - a namespaced user mapped to a key is not in a namespace,
655/// - no usable *N-Administrator* for a namespace is known,
656/// - the available keys in the namespace cannot be retrieved,
657/// - information about a specific key in the namespace cannot be retrieved,
658/// - a tag cannot be added to an already existing key,
659/// - a new key cannot be generated,
660/// - or using the default system-wide administrator again fails.
661///
662/// [a bug in the NetHSM firmware]: https://github.com/Nitrokey/nethsm/issues/13
663fn add_namespaced_keys(
664    nethsm: &NetHsm,
665    admin_credentials: &NetHsmAdminCredentials,
666    users: &[UserMapping],
667) -> Result<(), crate::Error> {
668    debug!(
669        "Setup namespaced cryptographic keys on NetHSM backend at {}",
670        nethsm.get_url()
671    );
672
673    // Use the default R-Administrator for authentication to the backend by default.
674    let default_admin = &admin_credentials.get_default_administrator()?.name;
675    nethsm.use_credentials(default_admin)?;
676
677    let available_users = nethsm.get_users()?;
678
679    for mapping in users {
680        for (user, key_setup, tag) in
681            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
682        {
683            let key_id = key_setup.get_key_id();
684            debug!("Set up key \"{key_id}\" with tag {tag} for user {user}");
685
686            // Extract the namespace from the user or return an error.
687            let Some(namespace) = user.namespace() else {
688                // Note: Returning this error is not really possible, as we are explicitly
689                // requesting tuples of namespaced user, key setup and tag.
690                return Err(Error::NamespaceUserNoNamespace { user }.into());
691            };
692
693            // Select the first available N-Administrator credentials for interacting with the
694            // NetHSM backend.
695            nethsm.use_credentials(&get_first_available_namespace_admin(
696                nethsm,
697                admin_credentials,
698                &available_users,
699                namespace,
700            )?)?;
701
702            let available_keys = nethsm.get_keys(None)?;
703
704            if available_keys.contains(&key_id) {
705                let key_info = nethsm.get_key(&key_id)?;
706
707                // Compare the key setups.
708                compare_key_setups(
709                    &key_id,
710                    Some(namespace),
711                    KeySetupComparison {
712                        state_type: StateType::SignstarConfig,
713                        key_type: key_setup.get_key_type(),
714                        key_mechanisms: HashSet::from_iter(key_setup.get_key_mechanisms()),
715                    },
716                    KeySetupComparison {
717                        state_type: StateType::NetHsm,
718                        key_type: key_info.r#type.into(),
719                        key_mechanisms: key_info.mechanisms.iter().map(Into::into).collect(),
720                    },
721                );
722
723                // If there are tags already, check if the tag we are looking for is already set and
724                // if so, skip to the next key.
725                if let Some(available_tags) = key_info.restrictions.tags {
726                    debug!(
727                        "Available tags for key \"{key_id}\" in namespace {namespace}: {}",
728                        available_tags.join(", ")
729                    );
730                    // NOTE: If the required tag is already set, continue to the next key.
731                    //       Without this we otherwise trigger a bug in the NetHSM firmware which
732                    //       breaks the connection after re-adding the tag for the key further down.
733                    //       (i.e. "Bad Status: HTTP version did not start with HTTP/")
734                    //       See https://github.com/Nitrokey/nethsm/issues/13 for details.
735                    if available_tags.len() == 1 && available_tags.contains(&tag) {
736                        continue;
737                    }
738                }
739
740                // Add the tag to the key.
741                nethsm.add_key_tag(&key_id, tag.as_str())?;
742            } else {
743                // Add the key, including the required tag.
744                nethsm.generate_key(
745                    key_setup.get_key_type(),
746                    key_setup.get_key_mechanisms(),
747                    key_setup.get_key_length(),
748                    Some(key_id.clone()),
749                    Some(vec![tag]),
750                )?;
751            }
752        }
753    }
754    // Always use the default R-Administrator again.
755    nethsm.use_credentials(default_admin)?;
756
757    Ok(())
758}
759
760/// Adds OpenPGP certificates for system-wide keys that are used for OpenPGP signing.
761///
762/// # Note
763///
764/// It is assumed that the [default
765/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all system-wide keys
766/// and all system-wide non-administrative users are already set up, before calling this function
767/// (see `add_system_wide_admins`, `add_system_wide_keys` and `add_non_administrative_users`,
768/// respectively).
769///
770/// This function uses the `nethsm` with the [default
771/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
772/// system-wide, non-administrative user for individual operations.
773/// If this function succeeds, the `nethsm` is guaranteed to use the [default
774/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
775/// If this function fails, the `nethsm` may still use a system-wide, non-administrative user.
776///
777/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
778/// inconsistencies between signatures created with a previous version of a certificate and those
779/// created with a new version of the certificate, which is hard to debug.
780///
781/// # Errors
782///
783/// Returns an error if
784///
785/// - using the default *R-Administrator* fails,
786/// - retrieving the names of all system-wide users fails,
787/// - retrieving the names of all system-wide keys fails,
788/// - a user used for OpenPGP signing does not exist,
789/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
790/// - a user used for OpenPGP signing does not have a required tag,
791/// - a key used for OpenPGP signing does not exist,
792/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
793/// - a key used for OpenPGP signing does not have a required tag,
794/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
795/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
796///   create an OpenPGP certificate for the key,
797/// - or the default *R-Administrator* cannot be used to import the generated OpenPGP certificate
798///   for the key.
799fn add_system_wide_openpgp_certificates(
800    nethsm: &NetHsm,
801    admin_credentials: &NetHsmAdminCredentials,
802    users: &[UserMapping],
803) -> Result<(), crate::Error> {
804    debug!(
805        "Setup OpenPGP certificates for system-wide cryptographic keys on NetHSM backend at {}",
806        nethsm.get_url()
807    );
808
809    // Use the default R-Administrator for authentication to the backend by default.
810    let default_admin = &admin_credentials.get_default_administrator()?.name;
811    nethsm.use_credentials(default_admin)?;
812
813    let available_users = nethsm.get_users()?;
814
815    for mapping in users {
816        // Continue to the next mapping if it is not used for signing.
817        if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
818            continue;
819        }
820
821        for (user, key_setup, tag) in
822            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::SystemWide)
823        {
824            // Get OpenPGP User IDs and version or continue to the next user/key setup if the
825            // mapping is not used for OpenPGP signing.
826            let CryptographicKeyContext::OpenPgp { user_ids, version } =
827                key_setup.get_key_context()
828            else {
829                debug!(
830                    "Skip creating an OpenPGP certificate for the key \"{}\" used by user \"{user}\" as it is not used in an OpenPGP context.",
831                    key_setup.get_key_id()
832                );
833                continue;
834            };
835
836            // Ensure the targeted user exists.
837            if !available_users.contains(&user) {
838                return Err(Error::UserMissing { user_id: user }.into());
839            }
840            // Ensure the required tag is assigned to the targeted user.
841            if !nethsm.get_user_tags(&user)?.contains(&tag) {
842                return Err(Error::UserMissingTag { user_id: user, tag }.into());
843            }
844
845            let available_keys = nethsm.get_keys(None)?;
846            let key_id = key_setup.get_key_id();
847
848            // Ensure the targeted key exists.
849            if !available_keys.contains(&key_id) {
850                return Err(Error::KeyMissing { key_id }.into());
851            }
852            // Ensure the required tag is assigned to the targeted key.
853            if !nethsm
854                .get_key(&key_id)?
855                .restrictions
856                .tags
857                .is_some_and(|tags| tags.contains(&tag))
858            {
859                return Err(Error::KeyIsMissingTag { key_id, tag }.into());
860            }
861
862            // Create the OpenPGP certificate if it does not exist yet.
863            if nethsm.get_key_certificate(&key_id)?.is_none() {
864                // Ensure the first OpenPGP User ID exists.
865                let Some(user_id) = user_ids.first() else {
866                    return Err(Error::OpenPgpUserIdMissing { key_id }.into());
867                };
868
869                // Switch to the dedicated user with access to the key to create an OpenPGP
870                // certificate for the key.
871                nethsm.use_credentials(&user)?;
872                let data = nethsm.create_openpgp_cert(
873                    &key_id,
874                    OpenPgpKeyUsageFlags::default(),
875                    user_id.clone(),
876                    Utc::now(),
877                    version,
878                )?;
879
880                // Switch back to the default R-Administrator for the import of the OpenPGP
881                // certificate.
882                nethsm.use_credentials(default_admin)?;
883                nethsm.import_key_certificate(&key_id, data)?;
884            }
885        }
886    }
887    // Always use the default R-Administrator again.
888    nethsm.use_credentials(default_admin)?;
889
890    Ok(())
891}
892
893/// Adds OpenPGP certificates for namespaced keys that are used for OpenPGP signing.
894///
895/// # Note
896///
897/// It is assumed that the [default
898/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], all namespaced keys,
899/// all _N-Administrators_ and all namespaced non-administrative users are already set up, before
900/// calling this function (see `add_system_wide_admins`, `add_namespaced_keys`,
901/// `add_namespace_admins` and `add_namespaced_non_administrative_users`, respectively).
902///
903/// This function uses the `nethsm` with the [default
904/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
905/// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
906/// If this function succeeds, the `nethsm` is guaranteed to use the [default
907/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
908/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
909/// non-administrative user.
910///
911/// This function does not overwrite or alter existing OpenPGP certificates, as this would introduce
912/// inconsistencies between signatures created with a previous version of a certificate and those
913/// created with a new version of the certificate, which is hard to debug.
914///
915/// # Errors
916///
917/// Returns an error if
918///
919/// - using the default *R-Administrator* fails,
920/// - retrieving the names of all users fails,
921/// - a namespaced user is not in a namespace,
922/// - no usable *N-Administrator* for a namespace is known,
923/// - a user used for OpenPGP signing does not exist,
924/// - the tags assigned to a user cannot be retrieved from the `nethsm`,
925/// - a user used for OpenPGP signing does not have a required tag,
926/// - retrieving the names of all keys in a namespace fails,
927/// - a key used for OpenPGP signing does not exist,
928/// - the tags assigned to a key cannot be retrieved from the `nethsm`,
929/// - a key used for OpenPGP signing does not have a required tag,
930/// - the key setup for a key used for OpenPGP signing does not have at least one User ID,
931/// - the user assigned the same tag as the key that is used for OpenPGP signing cannot be used to
932///   create an OpenPGP certificate for the key,
933/// - or the *N-Administrator* cannot be used to import the generated OpenPGP certificate for the
934///   key.
935fn add_namespaced_openpgp_certificates(
936    nethsm: &NetHsm,
937    admin_credentials: &NetHsmAdminCredentials,
938    users: &[UserMapping],
939) -> Result<(), crate::Error> {
940    debug!(
941        "Setup OpenPGP certificates for namespaced cryptographic keys on NetHSM backend at {}",
942        nethsm.get_url()
943    );
944
945    // Use the default R-Administrator for authentication to the backend by default.
946    let default_admin = &admin_credentials.get_default_administrator()?.name;
947    nethsm.use_credentials(default_admin)?;
948
949    let available_users = nethsm.get_users()?;
950
951    for mapping in users {
952        // Continue to the next mapping if it is not used for signing.
953        if !matches!(mapping, UserMapping::SystemNetHsmOperatorSigning { .. }) {
954            continue;
955        }
956
957        for (user, key_setup, tag) in
958            mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespaced)
959        {
960            // Get OpenPGP User IDs and version or continue to the next user/key setup if the
961            // mapping is not used for OpenPGP signing.
962            let CryptographicKeyContext::OpenPgp { user_ids, version } =
963                key_setup.get_key_context()
964            else {
965                continue;
966            };
967
968            // Extract the namespace from the user.
969            let Some(namespace) = user.namespace() else {
970                // Note: Returning this error is not really possible, as we are explicitly
971                // requesting tuples of namespaced user, key setup and tag.
972                return Err(Error::NamespaceUserNoNamespace { user }.into());
973            };
974
975            // Select the first available N-Administrator credentials for interacting with the
976            // NetHSM backend.
977            let admin = get_first_available_namespace_admin(
978                nethsm,
979                admin_credentials,
980                &available_users,
981                namespace,
982            )?;
983            nethsm.use_credentials(&admin)?;
984
985            // Ensure the targeted user exists.
986            if !available_users.contains(&user) {
987                return Err(Error::NamespaceUserMissing {
988                    user: user.clone(),
989                    namespace: namespace.clone(),
990                }
991                .into());
992            }
993            // Ensure the required tag is assigned to the targeted user.
994            let user_tags = nethsm.get_user_tags(&user)?;
995            if !user_tags.contains(&tag) {
996                return Err(Error::NamespaceUserMissingTag {
997                    user: user.clone(),
998                    namespace: namespace.clone(),
999                    tag,
1000                }
1001                .into());
1002            }
1003
1004            let available_keys = nethsm.get_keys(None)?;
1005            let key_id = key_setup.get_key_id();
1006
1007            // Ensure the targeted key exists.
1008            if !available_keys.contains(&key_id) {
1009                return Err(Error::NamespaceKeyMissing {
1010                    key_id,
1011                    namespace: namespace.clone(),
1012                }
1013                .into());
1014            }
1015            // Ensure the required tag is assigned to the targeted key.
1016            let pubkey = nethsm.get_key(&key_id)?;
1017            if !pubkey
1018                .restrictions
1019                .tags
1020                .is_some_and(|tags| tags.contains(&tag))
1021            {
1022                return Err(Error::NamespaceKeyMissesTag {
1023                    key_id,
1024                    namespace: namespace.clone(),
1025                    tag,
1026                }
1027                .into());
1028            }
1029
1030            // Create the OpenPGP certificate if it does not exist yet.
1031            if nethsm.get_key_certificate(&key_id)?.is_none() {
1032                // Ensure the first OpenPGP User ID exists.
1033                let Some(user_id) = user_ids.first() else {
1034                    return Err(Error::NamespaceOpenPgpUserIdMissing {
1035                        key_id,
1036                        namespace: namespace.clone(),
1037                    }
1038                    .into());
1039                };
1040
1041                // Switch to the dedicated user with access to the key to create an OpenPGP
1042                // certificate for the key.
1043                nethsm.use_credentials(&user)?;
1044                let data = nethsm.create_openpgp_cert(
1045                    &key_id,
1046                    OpenPgpKeyUsageFlags::default(),
1047                    user_id.clone(),
1048                    Utc::now(),
1049                    version,
1050                )?;
1051
1052                // Switch back to the N-Administrator for the import of the OpenPGP certificate.
1053                nethsm.use_credentials(&admin)?;
1054                nethsm.import_key_certificate(&key_id, data)?;
1055            }
1056        }
1057    }
1058    // Always use the default R-Administrator again.
1059    nethsm.use_credentials(default_admin)?;
1060
1061    Ok(())
1062}
1063
1064/// Retrieves the state for all users on a [`NetHsm`] backend.
1065///
1066/// # Note
1067///
1068/// Uses the `nethsm` with the [default
1069/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`].
1070///
1071/// # Errors
1072///
1073/// Returns an error if
1074///
1075/// - using the credentials of the default *R-Administrator* fails,
1076/// - retrieving all user names of the NetHSM backend fails,
1077/// - retrieving information about a specific NetHSM user fails,
1078/// - or retrieving the tags of an *Operator* user fails.
1079pub(crate) fn get_user_states(
1080    nethsm: &NetHsm,
1081    admin_credentials: &NetHsmAdminCredentials,
1082) -> Result<Vec<UserState>, crate::Error> {
1083    // Use the default R-Administrator.
1084    nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1085
1086    let mut users = Vec::new();
1087    for user_id in nethsm.get_users()? {
1088        let user_data = nethsm.get_user(&user_id)?;
1089        let tags = if user_data.role == UserRole::Operator.into() {
1090            nethsm.get_user_tags(&user_id)?
1091        } else {
1092            Vec::new()
1093        };
1094        users.push(UserState {
1095            name: user_id,
1096            role: user_data.role.into(),
1097            tags,
1098        });
1099    }
1100
1101    Ok(users)
1102}
1103
1104/// Retrieve the state of a key certificate.
1105///
1106/// Key certificates may be retrieved for system-wide keys or namespaced keys.
1107/// Returns a [`KeyCertificateState`], which may also encode reasons for why state cannot be
1108/// retrieved.
1109///
1110/// # Note
1111///
1112/// It is assumed that the current credentials for the `nethsm` provide access to the key
1113/// certificate of key `key_id`.
1114fn get_key_certificate_state(
1115    nethsm: &NetHsm,
1116    key_id: &KeyId,
1117    namespace: Option<&NamespaceId>,
1118) -> KeyCertificateState {
1119    // Provide a dedicated string for log messages in case a namespace is used.
1120    let namespace = if let Some(namespace) = namespace {
1121        format!(" in namespace \"{namespace}\"")
1122    } else {
1123        "".to_string()
1124    };
1125
1126    match nethsm.get_key_certificate(key_id) {
1127        Ok(Some(key_cert)) => {
1128            let public_key = match SignedPublicKey::from_reader_single(key_cert.as_slice()) {
1129                Ok((public_key, _armor_header)) => public_key,
1130                Err(error) => {
1131                    let message = format!(
1132                        "Unable to create OpenPGP certificate from key certificate of key \"{key_id}\"{namespace}:\n{error}"
1133                    );
1134                    debug!("{message}");
1135                    return KeyCertificateState::NotAnOpenPgpCertificate { message };
1136                }
1137            };
1138
1139            match TryInto::<CryptographicKeyContext>::try_into(public_key) {
1140                Ok(key_context) => KeyCertificateState::KeyContext(key_context),
1141                Err(error) => {
1142                    let message = format!(
1143                        "Unable to convert OpenPGP certificate of key \"{key_id}\"{namespace} to key context:\n{error}"
1144                    );
1145                    debug!("{message}");
1146                    KeyCertificateState::NotACryptographicKeyContext { message }
1147                }
1148            }
1149        }
1150        Ok(None) => KeyCertificateState::Empty,
1151        Err(error) => {
1152            let message = error.to_string();
1153            debug!("{message}");
1154            KeyCertificateState::Error { message }
1155        }
1156    }
1157}
1158
1159/// Retrieves the state for all keys on a [`NetHsm`] backend.
1160///
1161/// Collects each key, their [`KeyType`] and list of [`KeyMechanisms`][`KeyMechanism`].
1162/// Also attempts to derive a [`CryptographicKeyContext`] from the key certificate.
1163///
1164/// # Note
1165///
1166/// This function uses the `nethsm` with the [default
1167/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1168/// namespace-specific _N-Administrator_ for individual operations.
1169/// If this function succeeds, the `nethsm` is guaranteed to use the [default
1170/// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1171/// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1172///
1173///
1174/// # Errors
1175///
1176/// Returns an error if
1177///
1178/// - using the default *R-Administrator* for authentication against the backend fails,
1179/// - retrieving the names of all system-wide keys on the backend fails,
1180/// - retrieving information on a specific system-wide key on the backend fails,
1181/// - an *N-Administrator* in `admin_credentials` is not actually in a namespace,
1182/// - using the credentials of an *N-Administrator* fails,
1183/// - retrieving the names of all namespaced keys on the backend fails,
1184/// - or retrieving information on a specific namespaced key on the backend fails.
1185fn get_key_states(
1186    nethsm: &NetHsm,
1187    admin_credentials: &NetHsmAdminCredentials,
1188) -> Result<Vec<KeyState>, crate::Error> {
1189    // Use the default administrator
1190    let default_admin = &admin_credentials.get_default_administrator()?.name;
1191    nethsm.use_credentials(default_admin)?;
1192
1193    let mut keys = Vec::new();
1194    // Get the state of system-wide keys.
1195    for key_id in nethsm.get_keys(None)? {
1196        let key = nethsm.get_key(&key_id)?;
1197        let key_context = get_key_certificate_state(nethsm, &key_id, None);
1198
1199        keys.push(KeyState {
1200            name: key_id,
1201            namespace: None,
1202            tags: key.restrictions.tags.unwrap_or_default(),
1203            key_type: key.r#type.into(),
1204            mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1205            key_cert_state: key_context,
1206        });
1207    }
1208
1209    let mut seen_namespaces = HashSet::new();
1210    // Get the state of namespaced keys.
1211    for user_id in admin_credentials
1212        .get_namespace_administrators()
1213        .iter()
1214        .map(|creds| creds.name.clone())
1215    {
1216        // Extract the namespace of the user and ensure that the namespace exists already.
1217        let Some(namespace) = user_id.namespace() else {
1218            return Err(Error::NamespaceUserNoNamespace {
1219                user: user_id.clone(),
1220            }
1221            .into());
1222        };
1223
1224        // Only extract key information for the namespace if we have not already looked at it.
1225        if seen_namespaces.contains(namespace) {
1226            continue;
1227        }
1228        seen_namespaces.insert(namespace.clone());
1229
1230        nethsm.use_credentials(&user_id)?;
1231        for key_id in nethsm.get_keys(None)? {
1232            let key = nethsm.get_key(&key_id)?;
1233            let key_context = get_key_certificate_state(nethsm, &key_id, Some(namespace));
1234
1235            keys.push(KeyState {
1236                name: key_id,
1237                namespace: Some(namespace.clone()),
1238                tags: key.restrictions.tags.unwrap_or_default(),
1239                key_type: key.r#type.into(),
1240                mechanisms: key.mechanisms.iter().map(KeyMechanism::from).collect(),
1241                key_cert_state: key_context,
1242            });
1243        }
1244    }
1245
1246    // Always use the default *R-Administrator* again.
1247    nethsm.use_credentials(default_admin)?;
1248
1249    Ok(keys)
1250}
1251
1252/// A NetHSM backend that provides full control over its data.
1253///
1254/// This backend allows full control over the data in a [`NetHsm`], to the extend that is configured
1255/// by the tracked [`NetHsmAdminCredentials`] and [`SignstarConfig`].
1256#[derive(Debug)]
1257pub struct NetHsmBackend<'a, 'b> {
1258    nethsm: NetHsm,
1259    admin_credentials: &'a NetHsmAdminCredentials,
1260    signstar_config: &'b SignstarConfig,
1261}
1262
1263impl<'a, 'b> NetHsmBackend<'a, 'b> {
1264    /// Creates a new [`NetHsmBackend`].
1265    ///
1266    /// # Errors
1267    ///
1268    /// Returns an error if
1269    ///
1270    /// - the iteration of the `admin_credentials` does not match that of the `signstar_config`,
1271    /// - or retrieving the default administrator from the `admin_credentials` fails.
1272    ///
1273    /// # Examples
1274    ///
1275    /// ```
1276    /// use std::collections::HashSet;
1277    ///
1278    /// use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1279    /// use signstar_config::{
1280    ///     NetHsmAdminCredentials,
1281    ///     AdministrativeSecretHandling,
1282    ///     BackendConnection,
1283    ///     NetHsmBackend,
1284    ///     NonAdministrativeSecretHandling,
1285    ///     SignstarConfig,
1286    ///     UserMapping,
1287    /// };
1288    ///
1289    /// # fn main() -> testresult::TestResult {
1290    /// // The NetHSM connection.
1291    /// let nethsm = NetHsm::new(
1292    ///     Connection::new(
1293    ///         "https://example.org/api/v1".try_into()?,
1294    ///         ConnectionSecurity::Unsafe,
1295    ///     ),
1296    ///     None,
1297    ///     None,
1298    ///     None,
1299    /// )?;
1300    /// // The administrative credentials.
1301    /// let admin_credentials = NetHsmAdminCredentials::new(
1302    ///     1,
1303    ///     "backup-passphrase".parse()?,
1304    ///     "unlock-passphrase".parse()?,
1305    ///     vec![FullCredentials::new(
1306    ///         "admin".parse()?,
1307    ///         "admin-passphrase".parse()?,
1308    ///     )],
1309    ///     vec![FullCredentials::new(
1310    ///         "ns1~admin".parse()?,
1311    ///         "ns1-admin-passphrase".parse()?,
1312    ///     )],
1313    /// )?;
1314    /// // The Signstar config.
1315    /// let signstar_config = SignstarConfig::new(
1316    ///     1,
1317    ///     AdministrativeSecretHandling::ShamirsSecretSharing,
1318    ///     NonAdministrativeSecretHandling::SystemdCreds,
1319    ///     HashSet::from([BackendConnection::NetHsm(Connection::new(
1320    ///         "https://localhost:8443/api/v1/".parse()?,
1321    ///         "Unsafe".parse()?,
1322    ///     ))]),
1323    ///     HashSet::from([
1324    ///         UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1325    ///         UserMapping::SystemOnlyShareDownload {
1326    ///             system_user: "ssh-share-down".parse()?,
1327    ///             ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1328    ///         },
1329    ///         UserMapping::SystemOnlyShareUpload {
1330    ///             system_user: "ssh-share-up".parse()?,
1331    ///             ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1332    ///         }]),
1333    /// )?;
1334    ///
1335    /// let nethsm_backend = NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config)?;
1336    /// # Ok(())
1337    /// # }
1338    /// ```
1339    pub fn new(
1340        nethsm: NetHsm,
1341        admin_credentials: &'a NetHsmAdminCredentials,
1342        signstar_config: &'b SignstarConfig,
1343    ) -> Result<Self, crate::Error> {
1344        debug!(
1345            "Create a new NetHSM backend for Signstar config at {}",
1346            nethsm.get_url()
1347        );
1348
1349        // Ensure that the iterations of administrative credentials and signstar config match.
1350        if admin_credentials.get_iteration() != signstar_config.get_iteration() {
1351            return Err(Error::IterationMismatch {
1352                admin_creds: admin_credentials.get_iteration(),
1353                signstar_config: signstar_config.get_iteration(),
1354            }
1355            .into());
1356        }
1357
1358        // Add all system-wide Administrators for the connection
1359        for user in admin_credentials.get_administrators() {
1360            nethsm.add_credentials(user.into());
1361        }
1362        // Add all namespace Administrators for the connection
1363        for user in admin_credentials.get_namespace_administrators() {
1364            nethsm.add_credentials(user.into());
1365        }
1366        // Use the default administrator
1367        nethsm.use_credentials(&admin_credentials.get_default_administrator()?.name)?;
1368
1369        Ok(Self {
1370            nethsm,
1371            admin_credentials,
1372            signstar_config,
1373        })
1374    }
1375
1376    /// Creates a new [`State`] for the [`NetHsm`] backend.
1377    ///
1378    /// # Note
1379    ///
1380    /// This function uses the `nethsm` with the [default
1381    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1382    /// namespace-specific _N-Administrator_ for individual operations.
1383    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1384    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1385    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
1386    ///
1387    /// # Errors
1388    ///
1389    /// Returns an error if
1390    ///
1391    /// - retrieving the system state of the tracked [`NetHsm`] fails,
1392    /// - unlocking a locked [`NetHsm`] backend fails,
1393    /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
1394    pub fn state(&self) -> Result<State, crate::Error> {
1395        debug!(
1396            "Retrieve state of the NetHSM backend at {}",
1397            self.nethsm.get_url()
1398        );
1399
1400        let (users, keys) = match self.nethsm.state()? {
1401            SystemState::Unprovisioned => {
1402                debug!(
1403                    "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
1404                    self.nethsm.get_url()
1405                );
1406
1407                (Vec::new(), Vec::new())
1408            }
1409            SystemState::Locked => {
1410                debug!(
1411                    "Locked NetHSM backend detected at {}",
1412                    self.nethsm.get_url()
1413                );
1414
1415                self.nethsm.unlock(Passphrase::new(
1416                    self.admin_credentials.get_unlock_passphrase().into(),
1417                ))?;
1418
1419                let users = get_user_states(&self.nethsm, self.admin_credentials)?;
1420                let keys = get_key_states(&self.nethsm, self.admin_credentials)?;
1421                (users, keys)
1422            }
1423            SystemState::Operational => {
1424                debug!(
1425                    "Operational NetHSM backend detected at {}",
1426                    self.nethsm.get_url()
1427                );
1428
1429                let users = get_user_states(&self.nethsm, self.admin_credentials)?;
1430                let keys = get_key_states(&self.nethsm, self.admin_credentials)?;
1431                (users, keys)
1432            }
1433        };
1434
1435        Ok(State {
1436            state_type: StateType::NetHsm,
1437            users,
1438            keys,
1439        })
1440    }
1441
1442    /// Syncs the state of a Signstar configuration with the backend using credentials for users in
1443    /// non-administrative roles.
1444    ///
1445    /// Provisions unprovisioned NetHSM backends and unlocks locked ones.
1446    /// Then works down the following list to
1447    ///
1448    /// - create _R-Administrators_,
1449    ///     - or set their passphrase if they exist already,
1450    /// - create system-wide keys and add tags to them,
1451    ///     - or remove all tags from existing keys and only add the configured tags,
1452    /// - create users in the system-wide, non-administrative roles (i.e.
1453    ///   [`Backup`][`UserRole::Backup`], [`Metrics`][`UserRole::Metrics`] and
1454    ///   [`Operator`][`UserRole::Operator`]),
1455    ///     - or set their passphrase if they exist already,
1456    /// - create OpenPGP certificates for system-wide keys,
1457    ///     - or do nothing if they exist already,
1458    /// - create _N-Administrators_ and their respective namespaces,
1459    ///     - or set their passphrase if they exist already,
1460    /// - create namespaced keys and add tags to them,
1461    ///     - or remove all tags from existing keys and only add the configured tags,
1462    /// - create users in the namespaced, non-administrative roles (i.e.
1463    ///   [`Operator`][`UserRole::Operator`]),
1464    ///     - or set their passphrase if they exist already,
1465    /// - and create OpenPGP certificates for namespaced keys,
1466    ///     - or do nothing if they exist already.
1467    ///
1468    /// # Note
1469    ///
1470    /// This function uses the `nethsm` with the [default
1471    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
1472    /// namespace-specific _N-Administrator_ or non-administrative user for individual operations.
1473    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
1474    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
1475    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_ or
1476    /// non-administrative user.
1477    ///
1478    /// # Errors
1479    ///
1480    /// Returns an error if
1481    ///
1482    /// - retrieving the state of the [`NetHsm`] backend fails,
1483    /// - provisioning an unprovisioned [`NetHsm`] fails,
1484    /// - unlocking a locked [`NetHsm`] backend fails,
1485    /// - adding users in the system-wide [`Administrator`][`UserRole::Administrator`] role fails,
1486    /// - adding system-wide keys fails,
1487    /// - adding system-wide users in the [`Backup`][`UserRole::Backup`],
1488    ///   [`Metrics`][`UserRole::Metrics`] or [`Operator`][`UserRole::Operator`] role fails,
1489    /// - adding OpenPGP certificates for system-wide keys fails,
1490    /// - adding namespaced users in the [`Administrator`][`UserRole::Administrator`] role or adding
1491    ///   their respective namespace fails,
1492    /// - adding namespaced keys fails,
1493    /// - adding namespaced users in the [`Operator`][`UserRole::Operator`] role fails,
1494    /// - or adding OpenPGP certificates for namespaced keys fails.
1495    pub fn sync(&self, user_credentials: &[FullCredentials]) -> Result<(), crate::Error> {
1496        debug!(
1497            "Synchronize state of users and keys for the NetHSM backend at {} with the Signstar config.",
1498            self.nethsm.get_url()
1499        );
1500
1501        // Extract the user mappings.
1502        let users = self
1503            .signstar_config
1504            .iter_user_mappings()
1505            .cloned()
1506            .collect::<Vec<UserMapping>>();
1507
1508        match self.nethsm.state()? {
1509            SystemState::Unprovisioned => {
1510                debug!(
1511                    "Unprovisioned NetHSM backend detected at {}",
1512                    self.nethsm.get_url()
1513                );
1514
1515                self.nethsm.provision(
1516                    Passphrase::from_str(self.admin_credentials.get_unlock_passphrase())
1517                        .map_err(Error::NetHsmUser)?,
1518                    self.admin_credentials
1519                        .get_default_administrator()?
1520                        .passphrase
1521                        .clone(),
1522                    nethsm::Utc::now(),
1523                )?;
1524            }
1525            SystemState::Locked => {
1526                debug!(
1527                    "Locked NetHSM backend detected at {}",
1528                    self.nethsm.get_url()
1529                );
1530
1531                self.nethsm.unlock(Passphrase::new(
1532                    self.admin_credentials.get_unlock_passphrase().into(),
1533                ))?;
1534            }
1535            SystemState::Operational => {
1536                debug!(
1537                    "Operational NetHSM backend detected at {}",
1538                    self.nethsm.get_url()
1539                );
1540            }
1541        }
1542
1543        // Add any missing users and keys.
1544        add_system_wide_admins(&self.nethsm, self.admin_credentials)?;
1545        add_system_wide_keys(&self.nethsm, self.admin_credentials, &users)?;
1546        add_non_administrative_users(
1547            &self.nethsm,
1548            self.admin_credentials,
1549            &users,
1550            user_credentials,
1551        )?;
1552        add_system_wide_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1553        add_namespace_admins(&self.nethsm, self.admin_credentials)?;
1554        add_namespaced_keys(&self.nethsm, self.admin_credentials, &users)?;
1555        add_namespaced_non_administrative_users(
1556            &self.nethsm,
1557            self.admin_credentials,
1558            &users,
1559            user_credentials,
1560        )?;
1561        add_namespaced_openpgp_certificates(&self.nethsm, self.admin_credentials, &users)?;
1562
1563        Ok(())
1564    }
1565}
1566
1567#[cfg(test)]
1568mod tests {
1569    use std::collections::HashSet;
1570
1571    use log::LevelFilter;
1572    use nethsm::{Connection, ConnectionSecurity, FullCredentials, NetHsm};
1573    use signstar_common::logging::setup_logging;
1574    use testresult::TestResult;
1575
1576    use super::*;
1577    use crate::{
1578        AdministrativeSecretHandling,
1579        NonAdministrativeSecretHandling,
1580        config::base::BackendConnection,
1581    };
1582
1583    /// Ensures that the [`NetHsmBackend::new`] fails on mismatching iterations in
1584    /// [`NetHsmAdminCredentials`] and [`SignstarConfig`].
1585    #[test]
1586    fn nethsm_backend_new_fails_on_iteration_mismatch() -> TestResult {
1587        setup_logging(LevelFilter::Debug)?;
1588
1589        let nethsm = NetHsm::new(
1590            Connection::new(
1591                "https://example.org/api/v1".try_into()?,
1592                ConnectionSecurity::Unsafe,
1593            ),
1594            None,
1595            None,
1596            None,
1597        )?;
1598        // The administrative credentials.
1599        let admin_credentials = NetHsmAdminCredentials::new(
1600            // this is different from the one in the Signstar config.
1601            2,
1602            "backup-passphrase".parse()?,
1603            "unlock-passphrase".parse()?,
1604            vec![FullCredentials::new(
1605                "admin".parse()?,
1606                "admin-passphrase".parse()?,
1607            )],
1608            vec![FullCredentials::new(
1609                "ns1~admin".parse()?,
1610                "ns1-admin-passphrase".parse()?,
1611            )],
1612        )?;
1613        // The Signstar config.
1614        let signstar_config = SignstarConfig::new(
1615         1,
1616         AdministrativeSecretHandling::ShamirsSecretSharing,
1617         NonAdministrativeSecretHandling::SystemdCreds,
1618         HashSet::from([BackendConnection::NetHsm(Connection::new(
1619             "https://localhost:8443/api/v1/".parse()?,
1620             "Unsafe".parse()?,
1621         ))]),
1622         HashSet::from([
1623             UserMapping::NetHsmOnlyAdmin("admin".parse()?),
1624             UserMapping::SystemOnlyShareDownload {
1625                 system_user: "ssh-share-down".parse()?,
1626                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1627             },
1628             UserMapping::SystemOnlyShareUpload {
1629                 system_user: "ssh-share-up".parse()?,
1630                 ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh96uFTnvX6P1ebbLxXFvy6sK7qFqlMHDOuJ0TmuXQQ user@host".parse()?,
1631             }]),
1632     )?;
1633        let nethsm_backend_result =
1634            NetHsmBackend::new(nethsm, &admin_credentials, &signstar_config);
1635
1636        assert!(
1637            nethsm_backend_result.is_err(),
1638            "Test should have failed, but succeeded"
1639        );
1640        assert!(
1641            matches!(
1642                nethsm_backend_result,
1643                Err(crate::Error::NetHsmBackend(Error::IterationMismatch {
1644                    admin_creds: _,
1645                    signstar_config: _
1646                }))
1647            ),
1648            "Expected an `Error::IterationMismatch` but got {nethsm_backend_result:?}"
1649        );
1650
1651        Ok(())
1652    }
1653}