signstar_sign/
main.rs

1//! Application for the creation of signatures from signing requests.
2
3use std::process::ExitCode;
4
5use nethsm::{KeyId, NetHsm};
6use nethsm_config::UserMapping;
7use signstar_config::{CredentialsLoading, Error as ConfigError};
8use signstar_request_signature::{Request, Response, Sha512};
9
10/// Signstar signing error.
11#[derive(Debug, thiserror::Error)]
12enum Error {
13    /// Configuration does not contain key ID for the operator user.
14    #[error("No key ID set for the operator user")]
15    NoKeyId,
16
17    /// Loading configuration encountered errors.
18    #[error("Loading credentials encountered errors")]
19    HasUserIdErrors,
20
21    /// No credentials found for current system user.
22    #[error("No credentials for the system user")]
23    NoCredentials,
24
25    /// Parameters of the signing request are unsupported.
26    #[error("Unsupported signing request parameters")]
27    UnsupportedParameters,
28
29    /// Configuration error.
30    #[error("Config error")]
31    Config(#[from] ConfigError),
32
33    /// NetHSM error.
34    #[error("NetHsm error")]
35    NetHsm(#[from] nethsm::Error),
36
37    /// Signing request processing error.
38    #[error("Signing request error")]
39    SigningRequest(#[from] signstar_request_signature::Error),
40}
41
42/// Creates a new [`NetHsm`] object with correct connection and user settings and returns it
43/// alongside with the [`KeyId`] that should be used for signing.
44///
45/// # Errors
46///
47/// Returns an error if configuration:
48/// - loading encounters errors
49/// - does not contain any key ID settings
50/// - does not contain NetHSM connections
51/// - does not contain credentials with a passphrase
52fn load_nethsm_keyid() -> Result<(NetHsm, KeyId), Error> {
53    let credentials_loading = CredentialsLoading::from_system_user()?;
54
55    if credentials_loading.has_userid_errors() {
56        return Err(Error::HasUserIdErrors);
57    }
58
59    if !credentials_loading.has_signing_user() {
60        return Err(Error::NoCredentials);
61    }
62
63    let key_id = if let UserMapping::SystemNetHsmOperatorSigning {
64        nethsm_key_setup, ..
65    } = credentials_loading.get_mapping().get_user_mapping()
66    {
67        nethsm_key_setup.get_key_id().clone()
68    } else {
69        return Err(Error::NoKeyId);
70    };
71
72    // Currently, this picks the first connection found.
73    // The Signstar setup assumes, that multiple backends are used in a round-robin fashion, but
74    // this is not yet implemented.
75    let connection = if let Some(connection) = credentials_loading
76        .get_mapping()
77        .get_connections()
78        .iter()
79        .next()
80    {
81        connection.clone()
82    } else {
83        return Err(Error::NoCredentials);
84    };
85
86    let credentials = credentials_loading.credentials_for_signing_user()?;
87
88    Ok((
89        NetHsm::new(connection, Some(credentials.into()), None, None)?,
90        key_id,
91    ))
92}
93
94/// Signs the signing request in `reader` and write the response to the `writer`.
95///
96/// # Errors
97///
98/// Returns an error if:
99///
100/// - a [`Request`] can be created from `reader`,
101/// - the [`Request`] does not use OpenPGP v4,
102/// - the [`Request`] is not version 1,
103/// - a [`Sha512`] hasher state can not be created from the [`Request`],
104/// - no [`NetHsm`] and [`KeyId`] can be retrieved for the calling user,
105/// - a signature can not be created over the hasher state,
106/// - or the [`Response`] can not be written to the `writer`.
107fn sign_request(reader: impl std::io::Read, writer: impl std::io::Write) -> Result<(), Error> {
108    let req = Request::from_reader(reader)?;
109
110    if !req.required.output.is_openpgp_v4() {
111        Err(Error::UnsupportedParameters)?;
112    }
113
114    if req.version.major != 1 {
115        Err(Error::UnsupportedParameters)?;
116    }
117
118    let hasher: Sha512 = req.required.input.try_into()?;
119
120    let (nethsm, key_id) = load_nethsm_keyid()?;
121
122    let signature = nethsm.openpgp_sign_state(&key_id, hasher)?;
123
124    Response::v1(signature).to_writer(writer)?;
125
126    Ok(())
127}
128
129/// Signs the signing request on standard input and returns a signing response on standard output.
130fn main() -> ExitCode {
131    let result = sign_request(std::io::stdin(), std::io::stdout());
132
133    if let Err(error) = result {
134        eprintln!("{error}");
135        ExitCode::FAILURE
136    } else {
137        ExitCode::SUCCESS
138    }
139}