nethsm/key.rs
1use std::{fmt::Display, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4use signstar_crypto::key::MIN_RSA_BIT_LENGTH;
5
6use crate::TlsKeyType;
7
8/// An error that can occur when dealing with keys.
9#[derive(Debug, thiserror::Error)]
10pub enum Error {
11 /// Elliptic curve TLS keys do not support providing a length
12 #[error("Elliptic curve key ({tls_key_type}) does not support setting length")]
13 TlsKeyLengthUnsupported {
14 /// The type of TLS key.
15 tls_key_type: TlsKeyType,
16 },
17
18 /// RSA TLS key type requires setting a length
19 #[error("Generating a key of type {tls_key_type} requires setting a length")]
20 TlsKeyLengthRequired {
21 /// The type of TLS key.
22 tls_key_type: TlsKeyType,
23 },
24
25 /// RSA TLS key is generated with unsafe key length (smaller than 2048)
26 #[error(
27 "RSA keys shorter than {MIN_RSA_BIT_LENGTH} are not supported. A key length of {key_length} is unsafe!"
28 )]
29 InvalidTlsKeyLengthRsa {
30 /// The bit length of the RSA key.
31 key_length: u32,
32 },
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, Ord, PartialEq, PartialOrd, 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/// Ensures that a [`TlsKeyType`] is compatible with an optional key length
151///
152/// # Errors
153///
154/// Returns an [`Error::Key`][`crate::Error::Key`] if
155/// * `tls_key_type` is one of [`TlsKeyType::Curve25519`], [`TlsKeyType::EcP224`],
156/// [`TlsKeyType::EcP256`], [`TlsKeyType::EcP384`] or [`TlsKeyType::EcP521`] and `length` is
157/// [`Some`].
158/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is [`None`].
159/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is not [`Some`] value equal to or greater
160/// than [`MIN_RSA_BIT_LENGTH`].
161///
162/// # Examples
163///
164/// ```
165/// use nethsm::{TlsKeyType, tls_key_type_matches_length};
166///
167/// # fn main() -> testresult::TestResult {
168/// tls_key_type_matches_length(TlsKeyType::Curve25519, None)?;
169/// tls_key_type_matches_length(TlsKeyType::EcP224, None)?;
170/// tls_key_type_matches_length(TlsKeyType::Rsa, Some(2048))?;
171///
172/// // this fails because elliptic curve keys have their length set intrinsically
173/// assert!(tls_key_type_matches_length(TlsKeyType::Curve25519, Some(2048)).is_err());
174/// // this fails because a bit length of 1024 is unsafe to use for RSA keys
175/// assert!(tls_key_type_matches_length(TlsKeyType::Rsa, Some(1024)).is_err());
176/// # Ok(())
177/// # }
178/// ```
179pub fn tls_key_type_matches_length(
180 tls_key_type: TlsKeyType,
181 length: Option<u32>,
182) -> Result<(), Error> {
183 match tls_key_type {
184 TlsKeyType::Curve25519
185 | TlsKeyType::EcP224
186 | TlsKeyType::EcP256
187 | TlsKeyType::EcP384
188 | TlsKeyType::EcP521 => {
189 if length.is_some() {
190 Err(Error::TlsKeyLengthUnsupported { tls_key_type })
191 } else {
192 Ok(())
193 }
194 }
195 TlsKeyType::Rsa => match length {
196 None => Err(Error::TlsKeyLengthRequired { tls_key_type }),
197 Some(length) => {
198 if length < MIN_RSA_BIT_LENGTH {
199 Err(Error::InvalidTlsKeyLengthRsa { key_length: length })
200 } else {
201 Ok(())
202 }
203 }
204 },
205 }
206}