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}