signstar_crypto/key/
setup.rs

1//! Setup for signing keys.
2
3use serde::{Deserialize, Serialize};
4
5use crate::key::Error;
6use crate::key::{
7    CryptographicKeyContext,
8    KeyMechanism,
9    KeyType,
10    SignatureType,
11    key_type_and_mechanisms_match_signature_type,
12    key_type_matches_length,
13    key_type_matches_mechanisms,
14};
15
16/// The setup of a cryptographic signing key.
17///
18/// This covers the type of key, its supported mechanisms, its optional length, its signature type
19/// and the context in which it is used.
20#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
21pub struct SigningKeySetup {
22    key_type: KeyType,
23    key_mechanisms: Vec<KeyMechanism>,
24    key_length: Option<u32>,
25    signature_type: SignatureType,
26    key_context: CryptographicKeyContext,
27}
28
29impl SigningKeySetup {
30    /// Creates a new [`SigningKeySetup`].
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use signstar_crypto::{
36    ///     key::{CryptographicKeyContext, KeyMechanism, KeyType, SignatureType, SigningKeySetup},
37    ///     openpgp::OpenPgpUserIdList,
38    /// };
39    ///
40    /// # fn main() -> testresult::TestResult {
41    /// SigningKeySetup::new(
42    ///     KeyType::Curve25519,
43    ///     vec![KeyMechanism::EdDsaSignature],
44    ///     None,
45    ///     SignatureType::EdDsa,
46    ///     CryptographicKeyContext::Raw,
47    /// )?;
48    ///
49    /// SigningKeySetup::new(
50    ///     KeyType::Curve25519,
51    ///     vec![KeyMechanism::EdDsaSignature],
52    ///     None,
53    ///     SignatureType::EdDsa,
54    ///     CryptographicKeyContext::OpenPgp {
55    ///         user_ids: OpenPgpUserIdList::new(vec![
56    ///             "Foobar McFooface <foobar@mcfooface.org>".parse()?,
57    ///         ])?,
58    ///         version: "v4".parse()?,
59    ///     },
60    /// )?;
61    ///
62    /// // this fails because Curve25519 does not support the ECDSA key mechanism
63    /// assert!(
64    ///     SigningKeySetup::new(
65    ///         KeyType::Curve25519,
66    ///         vec![KeyMechanism::EcdsaSignature],
67    ///         None,
68    ///         SignatureType::EdDsa,
69    ///         CryptographicKeyContext::OpenPgp {
70    ///             user_ids: OpenPgpUserIdList::new(vec![
71    ///                 "Foobar McFooface <foobar@mcfooface.org>".parse()?
72    ///             ])?,
73    ///             version: "v4".parse()?,
74    ///         },
75    ///     )
76    ///     .is_err()
77    /// );
78    /// # Ok(())
79    /// # }
80    /// ```
81    ///
82    /// # Errors
83    ///
84    /// Returns an error if
85    ///
86    /// - the `key_type` and `key_mechanisms` are incompatible,
87    /// - the `key_type` and `key_length` are incompatible,
88    /// - the `key_type`, `key_mechanisms` and `signature_type` are incompatible,
89    /// - or the `cryptographic_key_context` is not valid.
90    pub fn new(
91        key_type: KeyType,
92        key_mechanisms: Vec<KeyMechanism>,
93        key_length: Option<u32>,
94        signature_type: SignatureType,
95        cryptographic_key_context: CryptographicKeyContext,
96    ) -> Result<Self, Error> {
97        key_type_matches_mechanisms(key_type, &key_mechanisms)?;
98        key_type_matches_length(key_type, key_length)?;
99        key_type_and_mechanisms_match_signature_type(key_type, &key_mechanisms, signature_type)?;
100        cryptographic_key_context.validate_signing_key_setup(
101            key_type,
102            &key_mechanisms,
103            signature_type,
104        )?;
105
106        Ok(Self {
107            key_type,
108            key_mechanisms,
109            key_length,
110            signature_type,
111            key_context: cryptographic_key_context,
112        })
113    }
114
115    /// Returns the [`KeyType`].
116    pub fn key_type(&self) -> KeyType {
117        self.key_type
118    }
119
120    /// Returns a reference to the list of [`KeyMechanism`]s.
121    pub fn key_mechanisms(&self) -> &[KeyMechanism] {
122        &self.key_mechanisms
123    }
124
125    /// Returns the optional key length.
126    pub fn key_length(&self) -> Option<u32> {
127        self.key_length
128    }
129
130    /// Returns the [`SignatureType`].
131    pub fn signature_type(&self) -> SignatureType {
132        self.signature_type
133    }
134
135    /// Returns a reference to the [`CryptographicKeyContext`].
136    pub fn key_context(&self) -> &CryptographicKeyContext {
137        &self.key_context
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use rstest::rstest;
144    use testresult::TestResult;
145
146    use super::*;
147
148    #[test]
149    fn signing_key_setup_new_succeeds() -> TestResult {
150        let setup = SigningKeySetup::new(
151            KeyType::Curve25519,
152            vec![KeyMechanism::EdDsaSignature],
153            None,
154            SignatureType::EdDsa,
155            CryptographicKeyContext::Raw,
156        )?;
157
158        assert_eq!(setup.key_type(), KeyType::Curve25519);
159        assert_eq!(setup.key_mechanisms(), [KeyMechanism::EdDsaSignature]);
160        assert_eq!(setup.key_length(), None);
161        assert_eq!(setup.signature_type(), SignatureType::EdDsa);
162        assert_eq!(setup.key_context(), &CryptographicKeyContext::Raw);
163
164        Ok(())
165    }
166
167    #[rstest]
168    #[case::curve25519_ecdsa(KeyType::Curve25519, vec![KeyMechanism::EcdsaSignature])]
169    #[case::rsa_ecdsa(KeyType::Rsa, vec![KeyMechanism::EcdsaSignature])]
170    fn signing_key_setup_new_fails_on_key_type_mechanism_mismatch(
171        #[case] key_type: KeyType,
172        #[case] key_mechanisms: Vec<KeyMechanism>,
173    ) -> TestResult {
174        let result = SigningKeySetup::new(
175            key_type,
176            key_mechanisms,
177            None,
178            SignatureType::EdDsa,
179            CryptographicKeyContext::Raw,
180        );
181
182        match result {
183            Err(Error::InvalidKeyMechanism { .. }) => {}
184            Err(error) => {
185                return Err(
186                    format!("Expected an Error::InvalidKeyMechanism, but got {error}").into(),
187                );
188            }
189            Ok(setup) => {
190                return Err(format!(
191                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
192                )
193                .into());
194            }
195        }
196
197        Ok(())
198    }
199
200    #[rstest]
201    #[case::curve25519_with_length(KeyType::Curve25519, vec![KeyMechanism::EdDsaSignature], Some(1024))]
202    #[case::ecp521_with_length(KeyType::EcP521, vec![KeyMechanism::EcdsaSignature], Some(1024))]
203    fn signing_key_setup_new_fails_on_key_length_unsupported(
204        #[case] key_type: KeyType,
205        #[case] key_mechanisms: Vec<KeyMechanism>,
206        #[case] key_length: Option<u32>,
207    ) -> TestResult {
208        let result = SigningKeySetup::new(
209            key_type,
210            key_mechanisms,
211            key_length,
212            SignatureType::EdDsa,
213            CryptographicKeyContext::Raw,
214        );
215
216        match result {
217            Err(Error::KeyLengthUnsupported { .. }) => {}
218            Err(error) => {
219                return Err(
220                    format!("Expected an Error::KeyLengthUnsupported, but got {error}").into(),
221                );
222            }
223            Ok(setup) => {
224                return Err(format!(
225                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
226                )
227                .into());
228            }
229        }
230
231        Ok(())
232    }
233
234    #[rstest]
235    #[case::rsa_too_short(KeyType::Rsa, vec![KeyMechanism::RsaSignaturePkcs1], Some(1024))]
236    #[case::rsa_no_length(KeyType::Rsa, vec![KeyMechanism::RsaSignaturePkcs1], None)]
237    fn signing_key_setup_new_fails_on_key_length_required_or_too_short(
238        #[case] key_type: KeyType,
239        #[case] key_mechanisms: Vec<KeyMechanism>,
240        #[case] key_length: Option<u32>,
241    ) -> TestResult {
242        let result = SigningKeySetup::new(
243            key_type,
244            key_mechanisms,
245            key_length,
246            SignatureType::EdDsa,
247            CryptographicKeyContext::Raw,
248        );
249
250        match result {
251            Err(Error::KeyLengthRequired { .. }) | Err(Error::InvalidKeyLengthRsa { .. }) => {}
252            Err(error) => {
253                return Err(
254                    format!("Expected an Error::KeyLengthRequired or Error::InvalidKeyLengthRsa, but got {error}").into(),
255                );
256            }
257            Ok(setup) => {
258                return Err(format!(
259                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
260                )
261                .into());
262            }
263        }
264
265        Ok(())
266    }
267
268    #[rstest]
269    #[case::curve25519_ecdsap521(KeyType::Curve25519, vec![KeyMechanism::EdDsaSignature], SignatureType::EcdsaP521)]
270    #[case::ecdsap521_eddsa(KeyType::EcP521, vec![KeyMechanism::EcdsaSignature], SignatureType::EdDsa)]
271    fn signing_key_setup_new_fails_signature_type_mismatch(
272        #[case] key_type: KeyType,
273        #[case] key_mechanisms: Vec<KeyMechanism>,
274        #[case] signature_type: SignatureType,
275    ) -> TestResult {
276        let result = SigningKeySetup::new(
277            key_type,
278            key_mechanisms,
279            None,
280            signature_type,
281            CryptographicKeyContext::Raw,
282        );
283
284        match result {
285            Err(Error::InvalidKeyTypeForSignatureType { .. })
286            | Err(Error::InvalidKeyMechanismsForSignatureType { .. }) => {}
287            Err(error) => {
288                return Err(
289                    format!("Expected an Error::InvalidKeyTypeForSignatureType or Error::InvalidKeyMechanismsForSignatureType, but got {error}").into(),
290                );
291            }
292            Ok(setup) => {
293                return Err(format!(
294                    "Should have failed, but succeeded in creating a SigningKeySetup: {setup:?}"
295                )
296                .into());
297            }
298        }
299
300        Ok(())
301    }
302}