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 { tls_key_type: TlsKeyType },
14
15 /// RSA TLS key type requires setting a length
16 #[error("Generating a key of type {tls_key_type} requires setting a length")]
17 TlsKeyLengthRequired { tls_key_type: TlsKeyType },
18
19 /// RSA TLS key is generated with unsafe key length (smaller than 2048)
20 #[error(
21 "RSA keys shorter than {MIN_RSA_BIT_LENGTH} are not supported. A key length of {key_length} is unsafe!"
22 )]
23 InvalidTlsKeyLengthRsa { key_length: u32 },
24
25 /// One or more [`KeyId`]s are not valid.
26 #[error("Invalid Key ID{}: {}", if key_ids.len() == 1 {"s"} else { " "}, key_ids.join(", "))]
27 InvalidKeyIds {
28 /// A list of strings representing invalid [`KeyId`]s.
29 key_ids: Vec<String>,
30 },
31
32 /// A signstar_crypto key error.
33 #[error("A signstar_crypto::key error:\n{0}")]
34 SignstarCryptoKey(#[from] signstar_crypto::key::Error),
35}
36
37/// A unique key identifier for a private key on a NetHSM.
38///
39/// A [`KeyId`]s must be in the character set `[a-z0-9]` and must not be empty.
40/// It is used in [key management] on a NetHSM and is unique in its scope.
41/// The same [`KeyId`] may exist system-wide and in one or several [namespaces], but no duplicate
42/// [`KeyId`] can exist system-wide or in the same namespace.
43///
44/// [key management]: https://docs.nitrokey.com/nethsm/operation#key-management
45/// [namespaces]: https://docs.nitrokey.com/nethsm/administration#namespaces
46#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
47#[serde(into = "String", try_from = "String")]
48pub struct KeyId(String);
49
50impl KeyId {
51 /// Constructs a new Key ID from a `String`.
52 ///
53 /// Validates the input string and returns [`crate::Error::Key`]
54 /// if it is invalid.
55 ///
56 /// # Errors
57 ///
58 /// Returns an [`Error`][`crate::Error`] if
59 /// * string contains characters outside of the allowed range (`[a-z0-9]`)
60 /// * string is empty
61 ///
62 /// # Examples
63 ///
64 /// ```
65 /// use nethsm::KeyId;
66 ///
67 /// assert!(KeyId::new("key1".into()).is_ok());
68 /// assert!(KeyId::new("key".into()).is_ok());
69 ///
70 /// // the input can not contain invalid chars
71 /// assert!(KeyId::new("key1#".into()).is_err());
72 /// assert!(KeyId::new("key~1".into()).is_err());
73 ///
74 /// // the key must be non-empty
75 /// assert!(KeyId::new("".into()).is_err());
76 /// ```
77 pub fn new(key_id: String) -> Result<Self, Error> {
78 if key_id.is_empty()
79 || !key_id.chars().all(|char| {
80 char.is_numeric() || (char.is_ascii_lowercase() && char.is_ascii_alphabetic())
81 })
82 {
83 return Err(Error::InvalidKeyIds {
84 key_ids: vec![key_id],
85 });
86 }
87
88 Ok(Self(key_id))
89 }
90}
91
92impl AsRef<str> for KeyId {
93 fn as_ref(&self) -> &str {
94 &self.0
95 }
96}
97
98impl From<KeyId> for String {
99 fn from(value: KeyId) -> Self {
100 value.0
101 }
102}
103
104impl FromStr for KeyId {
105 type Err = Error;
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 Self::new(s.into())
108 }
109}
110
111impl Display for KeyId {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 self.0.fmt(f)
114 }
115}
116
117impl TryFrom<&str> for KeyId {
118 type Error = Error;
119
120 fn try_from(value: &str) -> Result<Self, Self::Error> {
121 Self::from_str(value)
122 }
123}
124
125impl TryFrom<&String> for KeyId {
126 type Error = Error;
127
128 fn try_from(value: &String) -> Result<Self, Self::Error> {
129 Self::from_str(value)
130 }
131}
132
133impl TryFrom<String> for KeyId {
134 type Error = Error;
135
136 fn try_from(value: String) -> Result<Self, Self::Error> {
137 Self::new(value)
138 }
139}
140
141/// Ensures that a [`TlsKeyType`] is compatible with an optional key length
142///
143/// # Errors
144///
145/// Returns an [`Error::Key`][`crate::Error::Key`] if
146/// * `tls_key_type` is one of [`TlsKeyType::Curve25519`], [`TlsKeyType::EcP224`],
147/// [`TlsKeyType::EcP256`], [`TlsKeyType::EcP384`] or [`TlsKeyType::EcP521`] and `length` is
148/// [`Some`].
149/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is [`None`].
150/// * `tls_key_type` is [`TlsKeyType::Rsa`] and `length` is not [`Some`] value equal to or greater
151/// than [`MIN_RSA_BIT_LENGTH`].
152///
153/// # Examples
154///
155/// ```
156/// use nethsm::{TlsKeyType, tls_key_type_matches_length};
157///
158/// # fn main() -> testresult::TestResult {
159/// tls_key_type_matches_length(TlsKeyType::Curve25519, None)?;
160/// tls_key_type_matches_length(TlsKeyType::EcP224, None)?;
161/// tls_key_type_matches_length(TlsKeyType::Rsa, Some(2048))?;
162///
163/// // this fails because elliptic curve keys have their length set intrinsically
164/// assert!(tls_key_type_matches_length(TlsKeyType::Curve25519, Some(2048)).is_err());
165/// // this fails because a bit length of 1024 is unsafe to use for RSA keys
166/// assert!(tls_key_type_matches_length(TlsKeyType::Rsa, Some(1024)).is_err());
167/// # Ok(())
168/// # }
169/// ```
170pub fn tls_key_type_matches_length(
171 tls_key_type: TlsKeyType,
172 length: Option<u32>,
173) -> Result<(), Error> {
174 match tls_key_type {
175 TlsKeyType::Curve25519
176 | TlsKeyType::EcP224
177 | TlsKeyType::EcP256
178 | TlsKeyType::EcP384
179 | TlsKeyType::EcP521 => {
180 if length.is_some() {
181 Err(Error::TlsKeyLengthUnsupported { tls_key_type })
182 } else {
183 Ok(())
184 }
185 }
186 TlsKeyType::Rsa => match length {
187 None => Err(Error::TlsKeyLengthRequired { tls_key_type }),
188 Some(length) => {
189 if length < MIN_RSA_BIT_LENGTH {
190 Err(Error::InvalidTlsKeyLengthRsa { key_length: length })
191 } else {
192 Ok(())
193 }
194 }
195 },
196 }
197}