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