nethsm/
key.rs

1use std::{fmt::Display, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4use signstar_crypto::key::{
5    CryptographicKeyContext,
6    KeyMechanism,
7    KeyType,
8    MIN_RSA_BIT_LENGTH,
9    SignatureType,
10    key_type_and_mechanisms_match_signature_type,
11    key_type_matches_length,
12    key_type_matches_mechanisms,
13};
14
15use crate::TlsKeyType;
16
17/// An error that can occur when dealing with keys.
18#[derive(Debug, thiserror::Error)]
19pub enum Error {
20    /// Elliptic curve TLS keys do not support providing a length
21    #[error("Elliptic curve key ({tls_key_type}) does not support setting length")]
22    TlsKeyLengthUnsupported { tls_key_type: TlsKeyType },
23
24    /// RSA TLS key type requires setting a length
25    #[error("Generating a key of type {tls_key_type} requires setting a length")]
26    TlsKeyLengthRequired { tls_key_type: TlsKeyType },
27
28    /// RSA TLS key is generated with unsafe key length (smaller than 2048)
29    #[error(
30        "RSA keys shorter than {MIN_RSA_BIT_LENGTH} are not supported. A key length of {key_length} is unsafe!"
31    )]
32    InvalidTlsKeyLengthRsa { key_length: u32 },
33
34    /// One or more [`KeyId`]s are not valid.
35    #[error("Invalid Key ID{}: {}", if key_ids.len() == 1 {"s"} else { " "}, key_ids.join(", "))]
36    InvalidKeyIds {
37        /// A list of strings representing invalid [`KeyId`]s.
38        key_ids: Vec<String>,
39    },
40
41    /// A signstar_crypto key  error.
42    #[error("A signstar_crypto::key error:\n{0}")]
43    SignstarCryptoKey(#[from] signstar_crypto::key::Error),
44}
45
46/// A unique key identifier for a private key on a NetHSM.
47///
48/// A [`KeyId`]s must be in the character set `[a-z0-9]` and must not be empty.
49/// It is used in [key management] on a NetHSM and is unique in its scope.
50/// The same [`KeyId`] may exist system-wide and in one or several [namespaces], but no duplicate
51/// [`KeyId`] can exist system-wide or in the same namespace.
52///
53/// [key management]: https://docs.nitrokey.com/nethsm/operation#key-management
54/// [namespaces]: https://docs.nitrokey.com/nethsm/administration#namespaces
55#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
56#[serde(into = "String", try_from = "String")]
57pub struct KeyId(String);
58
59impl KeyId {
60    /// Constructs a new Key ID from a `String`.
61    ///
62    /// Validates the input string and returns [`crate::Error::Key`]
63    /// if it is invalid.
64    ///
65    /// # Errors
66    ///
67    /// Returns an [`Error`][`crate::Error`] if
68    /// * string contains characters outside of the allowed range (`[a-z0-9]`)
69    /// * string is empty
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use nethsm::KeyId;
75    ///
76    /// assert!(KeyId::new("key1".into()).is_ok());
77    /// assert!(KeyId::new("key".into()).is_ok());
78    ///
79    /// // the input can not contain invalid chars
80    /// assert!(KeyId::new("key1#".into()).is_err());
81    /// assert!(KeyId::new("key~1".into()).is_err());
82    ///
83    /// // the key must be non-empty
84    /// assert!(KeyId::new("".into()).is_err());
85    /// ```
86    pub fn new(key_id: String) -> Result<Self, Error> {
87        if key_id.is_empty()
88            || !key_id.chars().all(|char| {
89                char.is_numeric() || (char.is_ascii_lowercase() && char.is_ascii_alphabetic())
90            })
91        {
92            return Err(Error::InvalidKeyIds {
93                key_ids: vec![key_id],
94            });
95        }
96
97        Ok(Self(key_id))
98    }
99}
100
101impl AsRef<str> for KeyId {
102    fn as_ref(&self) -> &str {
103        &self.0
104    }
105}
106
107impl From<KeyId> for String {
108    fn from(value: KeyId) -> Self {
109        value.0
110    }
111}
112
113impl FromStr for KeyId {
114    type Err = Error;
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        Self::new(s.into())
117    }
118}
119
120impl Display for KeyId {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        self.0.fmt(f)
123    }
124}
125
126impl TryFrom<&str> for KeyId {
127    type Error = Error;
128
129    fn try_from(value: &str) -> Result<Self, Self::Error> {
130        Self::from_str(value)
131    }
132}
133
134impl TryFrom<&String> for KeyId {
135    type Error = Error;
136
137    fn try_from(value: &String) -> Result<Self, Self::Error> {
138        Self::from_str(value)
139    }
140}
141
142impl TryFrom<String> for KeyId {
143    type Error = Error;
144
145    fn try_from(value: String) -> Result<Self, Self::Error> {
146        Self::new(value)
147    }
148}
149
150/// The validated setup for a cryptographic signing key
151#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
152pub struct SigningKeySetup {
153    key_id: KeyId,
154    key_type: KeyType,
155    key_mechanisms: Vec<KeyMechanism>,
156    key_length: Option<u32>,
157    signature_type: SignatureType,
158    key_context: CryptographicKeyContext,
159}
160
161impl SigningKeySetup {
162    /// Creates a new [`SigningKeySetup`]
163    ///
164    /// # Errors
165    ///
166    /// Returns an [`Error::Key`][`crate::Error::Key`] if the key setup is not valid in itself or
167    /// can not be used for signing operations in the respective cryptographic context.
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use nethsm::{
173    ///     CryptographicKeyContext,
174    ///     KeyMechanism,
175    ///     KeyType,
176    ///     OpenPgpUserIdList,
177    ///     SignatureType,
178    ///     SigningKeySetup,
179    /// };
180    ///
181    /// # fn main() -> testresult::TestResult {
182    /// SigningKeySetup::new(
183    ///     "key1".parse()?,
184    ///     KeyType::Curve25519,
185    ///     vec![KeyMechanism::EdDsaSignature],
186    ///     None,
187    ///     SignatureType::EdDsa,
188    ///     CryptographicKeyContext::Raw,
189    /// )?;
190    ///
191    /// SigningKeySetup::new(
192    ///     "key1".parse()?,
193    ///     KeyType::Curve25519,
194    ///     vec![KeyMechanism::EdDsaSignature],
195    ///     None,
196    ///     SignatureType::EdDsa,
197    ///     CryptographicKeyContext::OpenPgp {
198    ///         user_ids: OpenPgpUserIdList::new(vec![
199    ///             "Foobar McFooface <foobar@mcfooface.org>".parse()?,
200    ///         ])?,
201    ///         version: "v4".parse()?,
202    ///     },
203    /// )?;
204    ///
205    /// // this fails because Curve25519 does not support the ECDSA key mechanism
206    /// assert!(
207    ///     SigningKeySetup::new(
208    ///         "key1".parse()?,
209    ///         KeyType::Curve25519,
210    ///         vec![KeyMechanism::EcdsaSignature],
211    ///         None,
212    ///         SignatureType::EdDsa,
213    ///         CryptographicKeyContext::OpenPgp {
214    ///             user_ids: OpenPgpUserIdList::new(vec![
215    ///                 "Foobar McFooface <foobar@mcfooface.org>".parse()?
216    ///             ])?,
217    ///             version: "v4".parse()?,
218    ///         },
219    ///     )
220    ///     .is_err()
221    /// );
222    /// # Ok(())
223    /// # }
224    /// ```
225    pub fn new(
226        key_id: KeyId,
227        key_type: KeyType,
228        key_mechanisms: Vec<KeyMechanism>,
229        key_length: Option<u32>,
230        signature_type: SignatureType,
231        cryptographic_key_context: CryptographicKeyContext,
232    ) -> Result<Self, Error> {
233        key_type_matches_mechanisms(key_type, &key_mechanisms)?;
234        key_type_matches_length(key_type, key_length)?;
235        key_type_and_mechanisms_match_signature_type(key_type, &key_mechanisms, signature_type)?;
236        cryptographic_key_context.validate_signing_key_setup(
237            key_type,
238            &key_mechanisms,
239            signature_type,
240        )?;
241
242        Ok(Self {
243            key_id,
244            key_type,
245            key_mechanisms,
246            key_length,
247            signature_type,
248            key_context: cryptographic_key_context,
249        })
250    }
251
252    /// Returns the [`KeyId`]
253    pub fn get_key_id(&self) -> KeyId {
254        self.key_id.clone()
255    }
256
257    /// Returns the [`KeyType`]
258    pub fn get_key_type(&self) -> KeyType {
259        self.key_type
260    }
261
262    /// Returns the list of [`KeyMechanism`]s
263    pub fn get_key_mechanisms(&self) -> Vec<KeyMechanism> {
264        self.key_mechanisms.clone()
265    }
266
267    /// Returns the optional key length
268    pub fn get_key_length(&self) -> Option<u32> {
269        self.key_length
270    }
271
272    /// Returns the [`SignatureType`]
273    pub fn get_signature_type(&self) -> SignatureType {
274        self.signature_type
275    }
276
277    /// Returns the [`CryptographicKeyContext`]
278    pub fn get_key_context(&self) -> CryptographicKeyContext {
279        self.key_context.clone()
280    }
281}
282
283/// Ensures that a [`TlsKeyType`] is compatible with an optional key length
284///
285/// # Errors
286///
287/// Returns an [`Error::Key`][`crate::Error::Key`] if
288/// * `tls_key_type` is one of [`TlsKeyType::Curve25519`], [`TlsKeyType::EcP224`],
289///   [`TlsKeyType::EcP256`], [`TlsKeyType::EcP384`] or [`TlsKeyType::EcP521`] and `length` is
290///   [`Some`].
291/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is [`None`].
292/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is not [`Some`] value equal to or greater
293///   than [`MIN_RSA_BIT_LENGTH`].
294///
295/// # Examples
296///
297/// ```
298/// use nethsm::{TlsKeyType, tls_key_type_matches_length};
299///
300/// # fn main() -> testresult::TestResult {
301/// tls_key_type_matches_length(TlsKeyType::Curve25519, None)?;
302/// tls_key_type_matches_length(TlsKeyType::EcP224, None)?;
303/// tls_key_type_matches_length(TlsKeyType::Rsa, Some(2048))?;
304///
305/// // this fails because elliptic curve keys have their length set intrinsically
306/// assert!(tls_key_type_matches_length(TlsKeyType::Curve25519, Some(2048)).is_err());
307/// // this fails because a bit length of 1024 is unsafe to use for RSA keys
308/// assert!(tls_key_type_matches_length(TlsKeyType::Rsa, Some(1024)).is_err());
309/// # Ok(())
310/// # }
311/// ```
312pub fn tls_key_type_matches_length(
313    tls_key_type: TlsKeyType,
314    length: Option<u32>,
315) -> Result<(), Error> {
316    match tls_key_type {
317        TlsKeyType::Curve25519
318        | TlsKeyType::EcP224
319        | TlsKeyType::EcP256
320        | TlsKeyType::EcP384
321        | TlsKeyType::EcP521 => {
322            if length.is_some() {
323                Err(Error::TlsKeyLengthUnsupported { tls_key_type })
324            } else {
325                Ok(())
326            }
327        }
328        TlsKeyType::Rsa => match length {
329            None => Err(Error::TlsKeyLengthRequired { tls_key_type }),
330            Some(length) => {
331                if length < MIN_RSA_BIT_LENGTH {
332                    Err(Error::InvalidTlsKeyLengthRsa { key_length: length })
333                } else {
334                    Ok(())
335                }
336            }
337        },
338    }
339}